Search code examples
pythonqtpyqtvtkqt-designer

Insert VTK into the right spot of layout


I am trying to put the VTK 3D scene widget into the right spot of pyqtgraphics.GraphicsLayout(). But either the scene does not appear if i am not assigning the window as parent or if I do - the scene does not adapt to the free area, it is just floating in the upper left corner.

I am currently struggling to find a solution to put a viewbox and vtkrenderer into the same window.

One of my attempts was to assign the self.w (see below this paragraph)- which is my main window as a parent to the renderer but then I don't know how to tell the renderer to place itself in the lower right corner of the window instead of floating in the upper left corner - which also leads to overlapping of other elements in the window.

Creating new window and assigning this window as parent

self.vtkWidget = QVTKRenderWindowInteractor(self.w_3d)

Using refered window as parent -> leads to floating and overlapping rendered scene

self.vtkWidget = QVTKRenderWindowInteractor(self.w)

enter image description here

import pyqtgraph as pg
import pyqtgraph.opengl as gl
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
from PyQt5 import Qt
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
import vtk, sys

class GUI:
    def __init__(self):
        self.init_gui() 

    def proxyWidget(self, item, width=None, height=None):
        proxy = QtGui.QGraphicsProxyWidget()
        if(height != None):
            height = item.sizeHint().height() if height==None else height
            item.setMaximumHeight(height)
        if(width!=None):
            width = item.sizeHint().width() if width==None else width
            item.setMaximumWidth(width)
        proxy.setWidget(item)
        return proxy

    def init_gui(self, win_height=800, win_width=1800):
        #self.w = self
        #self.w.scene().sigMouseClicked.connect(self.mousePressEvent) #mouseMoveEvent
        #self.w.scene().sigMouseMoved.connect(self.mouseMoveEvent)
        pg.setConfigOptions(imageAxisOrder='row-major')
        pg.setConfigOption('background', 'w')
        pg.setConfigOption('foreground', 'k')
        self.w = pg.GraphicsWindow(size=(win_width,win_height), border=True)
        self.img = pg.ImageItem()
        self.list_imgs       = QtGui.QListWidget()
        self.btn_Del_Mark    = QtGui.QPushButton('Del Mark')
        self.btn_MarkPed     = QtGui.QPushButton('Mark ped')
        self.lbl_list1       = QtGui.QLabel("List Images")
        self.lbl_list2       = QtGui.QLabel("List Markings")
        self.list_imgs       = QtGui.QListWidget()
        self.list_marks      = QtGui.QListWidget()
        self.layout = pg.GraphicsLayout()
        self.w_3d = pg.GraphicsWindow()
        self.vb = pg.ViewBox()


        self.lbl_list1.setAlignment(QtCore.Qt.AlignCenter)
        self.lbl_list2.setAlignment(QtCore.Qt.AlignCenter)
        self.vb.setAspectLocked()
        self.vb.addItem(self.img)
        self.vb.invertY(True)
        self.vtkWidget = QVTKRenderWindowInteractor(self.w_3d)
        self.w_3d.addItem(self.proxyWidget(self.vtkWidget))

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

        # Create source
        source = vtk.vtkSphereSource()
        source.SetCenter(0, 0, 0)
        source.SetRadius(5.0)

        # 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.Initialize()
        self.iren.Start()
        self.vtkWidget.show()

        self.layout.addItem(self.vb                             , 1,  3, 20,  20)
        self.layout.addItem(self.proxyWidget(self.lbl_list1     , width=(int(1./10.*win_width)), height=(int(0.9/20.*win_height))),  0,1,1,1)   
        self.layout.addItem(self.proxyWidget(self.lbl_list2     , width=(int(1./10.*win_width)), height=(int(0.9/20.*win_height))),  0,2,1,1)  
        self.layout.addItem(self.proxyWidget(self.list_imgs     , width=(int(1./10.*win_width))),  1,1,20,1)   
        self.layout.addItem(self.proxyWidget(self.list_marks    , width=(int(1./10.*win_width))),  1,2,20,1)   

        self.w.addItem(self.layout)

if __name__ == "__main__":
    app = QtGui.QApplication([])
    guiobj = GUI()

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

EDIT:

Currently I have solved it by having two windows - after several days of failed attempts. But even this looks crap - since the rendered scene floats in the window without reaction to window resize events...

enter image description here

I would like to have those two windows - in one:

enter image description here

One of the failed attempts was as follows - but then pyqt acquires space in the layout without showing the renderer scene in the window... - just empty space:

self.vtkWidget = QVTKRenderWindowInteractor() #No Parent
#...see rest of code in the section above with exception of the following 2 lines
self.layout.addItem(self.proxyWidget(self.vtkWidget), 10, 3, 10, 20)
self.vtkWidget.show()

Solution

  • I was able to solve it - I have replaced the QGraphicsLayout with QGridlayout and removed all proxywidget-wrappers and I found a workaround for adding a viewbox by using the plotwidget.

    I just need now to remove the axes - but this is secondary

    import pyqtgraph as pg
    import pyqtgraph.opengl as gl
    from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
    from PyQt5 import Qt
    from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
    import vtk, sys
    import numpy as np
    from PIL import Image
    class GUI:
        def __init__(self):
            self.init_gui() 
    
        def proxyWidget(self, item, width=None, height=None):
            proxy = QtGui.QGraphicsProxyWidget()
            if(height != None):
                height = item.sizeHint().height() if height==None else height
                item.setMaximumHeight(height)
            if(width!=None):
                width = item.sizeHint().width() if width==None else width
                item.setMaximumWidth(width)
            proxy.setWidget(item)
            return proxy
    
        def init_gui(self, win_height=800, win_width=1800):
            pg.setConfigOptions(imageAxisOrder='row-major')
            pg.setConfigOption('background', 'w')
            pg.setConfigOption('foreground', 'k')
            self.w = pg.GraphicsWindow(size=(win_width,win_height), border=True)
            self.img = pg.ImageItem()
            self.list_imgs       = QtGui.QListWidget()
            self.btn_Del_Mark    = QtGui.QPushButton('Del Mark')
            self.btn_MarkPed     = QtGui.QPushButton('Mark ped')
            self.lbl_list1       = QtGui.QLabel("List Images")
            self.lbl_list2       = QtGui.QLabel("List Markings")
            self.list_imgs       = QtGui.QListWidget()
            self.list_marks      = QtGui.QListWidget()
            self.layout = QtGui.QGridLayout()
            self.w.setLayout(self.layout)
            #self.w_3d = pg.GraphicsWindow()
    
            self.vtkWidget = QVTKRenderWindowInteractor()
            #self.w_3d.addItem(self.proxyWidget(self.vtkWidget))
    
            self.vtkWidget.Initialize()
            self.vtkWidget.Start()
            self.ren = vtk.vtkRenderer()
            self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
            self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()
    
            # Create source
            source = vtk.vtkSphereSource()
            source.SetCenter(0, 0, 0)
            source.SetRadius(5.0)
    
            # 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.Initialize()
            self.iren.Start()
            path = "/home/brain/uni/frustum-pointnets/dataset/KITTI/object/testing/image_2/000000.png"
            imgdata = Image.open(path)
            self.imgArr = np.array(imgdata)
            #ToDo: undistort Image if neccessary
    
            self.img.setImage(self.imgArr)
            #self.vbLayout = self.w.addLayout(row=0,  col=3, rowspan=10,  colspan=20)
            imageGraph = pg.PlotWidget(name='Signalgraph')
            self.vb = imageGraph.plotItem.vb
            self.lbl_list1.setAlignment(QtCore.Qt.AlignCenter)
            self.lbl_list2.setAlignment(QtCore.Qt.AlignCenter)
            self.vb.setAspectLocked()
            self.vb.addItem(self.img)
            self.vb.invertY(True)
            self.vb.setMaximumSize(int(7/10.*win_width), int(9/20.*win_height))
            self.layout.addWidget(imageGraph,                           1 , 3, 10,  20)
            self.layout.addWidget(self.vtkWidget                      , 11, 3, 10,  20)
            self.layout.addWidget(self.lbl_list1 ,                                  0, 1, 1, 1)   
            self.layout.addWidget(self.lbl_list2 ,                                  0, 2, 1, 1)  
            self.layout.addWidget(self.list_imgs ,                                  1, 1, 20,1)   
            self.layout.addWidget(self.list_marks,                                  1, 2, 20,1)   
            sizeHint =  lambda: pg.QtCore.QSize(int(1./10.*win_width), int(0.9/20.*win_height))
            self.lbl_list1.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(0.9/20.*win_height))
            self.lbl_list2.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(0.9/20.*win_height))
            self.list_imgs.sizeHint  = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(18/20.*win_height))
            self.list_marks.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(18/20.*win_height))
            self.list_imgs.setMaximumWidth(int(1./10.*win_width))
            self.list_marks.setMaximumWidth(int(1./10.*win_width))
    
            self.vtkWidget.show()
    
    
    if __name__ == "__main__":
        app = QtGui.QApplication([])
        guiobj = GUI()
    
        if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
            QtGui.QApplication.instance().exec_()