Search - User list
Full Version: SceneViewer as QWidget in H18
Root » Technical Discussion » SceneViewer as QWidget in H18
Alexey Vanzhula
Why we still don’t have a native way to get scene viewer area as Qt widget? Why it is not a problem for Maya and Modo for many years. After every major release I have to come up with a new way to fix the problem
Alexey Vanzhula
So, the changes in H18 broke my tools.

This code works well in H17.5, but gives an error in H18. Please help me.
import shiboken2
import PySide2.QtWidgets as qtw

def get_viewer_widget(): 
    main_widget = hou.qt.mainWindow()
    for widget in main_widget.findChildren(qtw.QWidget, 'RE_Window'):
        if widget.windowTitle() == 'DM_ViewLayout':
            for l in widget.findChildren(qtw.QVBoxLayout):
                if l.count()==1:
                    w = l.itemAt(0).widget()
                    if w.objectName() == 'RE_GLDrawable':
                        i = long(shiboken2.getCppPointer(w)[0])
                        mouse_widget = shiboken2.wrapInstance(i, qtw.QWidget)
                        return mouse_widget

Stackenblocken-Sidefx
What happens if you cast the widget pointer to int instead of long?
Alexey Vanzhula
Stackenblocken-Sidefx
What happens if you cast the widget pointer to int instead of long?
same result
pezetko
This could happen very easily in Qt/PySide if you forget to store unowned objects. The same thing could happen in Maya or Nuke.

If you call mouse_widget.width() inside your function you will get the correct value, but outside it will get this error.
You can check that by calling shiboken2.isValid() on your object:

print shiboken2.isValid(get_viewer_widget())

More about this in https://doc.qt.io/qtforpython/shiboken2/ownership.html#common-pitfalls [doc.qt.io]

If you rewrite the code like this it works (18.0.306, Windows 10 1909)
import hou
import shiboken2
from PySide2.QtWidgets import *

class ViewerWidget(object):

    def __init__(self):
        self.viewer = None
        self.main_window = None
    
    def get_viewer_widget(self):
        # store main window otherwise its widget are destroyed
        self.main_window = hou.qt.mainWindow()
        for widget in self.main_window.findChildren(QWidget, 'RE_Window'):
            if widget.windowTitle() == 'DM_ViewLayout':
                DM_ViewLayout = widget
                break
        else:
            print('Cannot find DM_ViewLayout')
            return

        for l in DM_ViewLayout.findChildren(QVBoxLayout):
            if l.count()!=1:
                continue

            w = l.itemAt(0).widget()
            if w.objectName() == 'RE_GLDrawable':
                i = long(shiboken2.getCppPointer(w)[0])
                self.viewer = shiboken2.wrapInstance(i, QWidget)
                return


x = ViewerWidget()
x.get_viewer_widget()
print(shiboken2.isValid(x.viewer))   # check if it is valid
print(x.viewer.width())
Alexey Vanzhula
Amazing! I'll try it a bit later. Thank you so much!
Alexey Vanzhula
@pezetko

Great! Even my function works. All I need is to declare main_widget as global variable. Thank you!
animatrix_
Do we have to install shiboken2 manually?

I am also doing something similar and everything seems to work with this:

def getViewportRenderViewPosSize():
    qtWindow = hou.ui.mainQtWindow()
    for w in qtWindow.findChildren(QtWidgets.QWidget, "RE_Window"):
        if w.windowTitle() == "UI_Viewport":
            for w2 in w.findChildren(QtWidgets.QVBoxLayout):
                if w2.count() == 1:
                    w = w2.itemAt(0).widget()
                    if  w.objectName() == 'RE_GLDrawable':
                        return w.mapToGlobal(w.pos()), w.size()
        if w.windowTitle() == "DM_ViewLayout":
            for w2 in w.findChildren(QtWidgets.QVBoxLayout):
                if w2.count() == 1:
                    w = w2.itemAt(0).widget()
                    if w.objectName() == 'RE_GLDrawable':
                        return w.mapToGlobal(w.pos()), w.size()
                        
    return None, None


But as soon as I MMB a node, Houdini crashes with this:

Qt Warn: External WM_DESTROY received for  QWidgetWindow(0xabc3e320, name="RE_WindowWindow") , parent:  QWindow(0x0) , transient parent:  QWindow(0x0)
Qt Warn: External WM_DESTROY received for  QWidgetWindow(0x6c4ba2c0, name="RE_WindowWindow") , parent:  QWindow(0x0) , transient parent:  QWindow(0x0)
Traceback (most recent call last):
  File "C:/PROGRA~1/SIDEEF~1/HOUDIN~1.287/houdini/python2.7libs\nodegraph.py", line 165, in handleEventCoroutine
    pending_actions)
  File "C:/PROGRA~1/SIDEEF~1/HOUDIN~1.287/houdini/python2.7libs\nodegraph.py", line 1240, in handleEvent
    uievent.editor, output_index)
  File "C:/PROGRA~1/SIDEEF~1/HOUDIN~1.287/houdini/python2.7libs\nodegraphui.py", line 1426, in createInfoWindow
    force_cook, output_index)
  File "C:/PROGRA~1/SIDEEF~1/HOUDIN~1.287/houdini/python2.7libs\nodegraphui.py", line 868, in display
    self.update(node, pinnable, recook, output_index)
  File "C:/PROGRA~1/SIDEEF~1/HOUDIN~1.287/houdini/python2.7libs\nodegraphui.py", line 922, in update
    verbose=verbose, debug=debug)
  File "C:/PROGRA~1/SIDEEF~1/HOUDIN~1.287/houdini/python2.7libs\nodegraphui.py", line 452, in update
    templatefile, showall=self.showall, **kwargs
  File "C:/PROGRA~1/SIDEEF~1/HOUDIN~1.287/houdini/python2.7libs\nodegraphui.py", line 428, in update
    self.set_html(html)
  File "C:/PROGRA~1/SIDEEF~1/HOUDIN~1.287/houdini/python2.7libs\nodegraphui.py", line 414, in set_html
    self.htmlarea.setHtml(html)
  File "C:/PROGRA~1/SIDEEF~1/HOUDIN~1.287/houdini/python2.7libs\nodegraphui.py", line 155, in setHtml
    super(HtmlArea, self).setHtml(html)
RuntimeError: Internal C++ object (HtmlArea) already deleted.


Any ideas?
animatrix_
Also there are some other issues I came across with Houdini's qt:

1. If you run this inside 123.py, nothing happens.

def initializeSession():
	window = hou.ui.mainQtWindow()
	window.showFullScreen()
	

hdefereval.execute_deferred(initializeSession)

Normally should have made Houdini application full screen. If you run this code, after Houdini has finished initializing, it works. So there seems to be some kind of delay or problem order of operations. When I print window in 123.py, there is a valid handle.


2. To get a qtwidget handle for a floating window in the current session, I wrote this:

name = "my_panel_name"
app = QtGui.QGuiApplication.instance()

mypanel = None
allWidgets = app.allWidgets()
for w in allWidgets:
    if name in w.windowTitle():
        mypanel  = w
        break;

This works but, if I create the floating panel in the same code and this code comes after, it doesn't find the floating panel. I have to run the code again for it to find the qt widget. Stuff like this complicates this significantly and makes everything more fragile due to many moving parts.

If anyone knows solutions for these, I would appreciate it.
houGenie
animatrix_
o install shiboken2 manually?
hi animatrix,

no shiboken2 is already installed. you can import it with “import shiboken2”. at least it works for me with a clean houdini 18 installation.
Alexey Vanzhula
H17.5:
from hutil.Qt import shiboken2

H18.0:
import shiboken2
animatrix_
Thanks a lot guys, shiboken2 is a very strange module name but it works
julien-b
Hi guys,

I'm trying to do the same for the network Editor, but even going (recursively) through all the Qt widgets from the hou.qt.mainWindow(), I can't find the right widget …

Any thought ?

Thanks a lot,

Julien
animatrix_
julien-b
Hi guys,

I'm trying to do the same for the network Editor, but even going (recursively) through all the Qt widgets from the hou.qt.mainWindow(), I can't find the right widget ...

Any thought ?

Thanks a lot,

Julien

This is easy to do, you just have to make sure you know the name of the pane you are looking for or set the name yourself

From there I call this function to get the qt handle:

def getWidgetByName(name):
    if not hasattr(hou.session, "mainQtWindow"):
        hou.session.mainQtWindow = hou.qt.mainWindow()

    hasHandle = hasattr(hou.session, name)
    if not hasHandle or (hasHandle and getattr(hou.session, name) and not shiboken2.isValid(getattr(hou.session, name))):
        allWidgets = QtWidgets.QApplication.allWidgets()
        for w in allWidgets:
            if name in w.windowTitle():
                i = int(shiboken2.getCppPointer(w)[0])
                qw = shiboken2.wrapInstance(i, QtWidgets.QWidget)

                setattr(hou.session, name, qw)
                w.setParent(hou.session.mainQtWindow, QtCore.Qt.Tool)
                break

    if not hasattr(hou.session, name):
        return None

    return getattr(hou.session, name)


Ideally it should be easier to do this by using pane.qthandle() but we are not there yet.
mabelzile
FYI, hou.SceneViewer.qtWindow() was added in 19.0
https://www.sidefx.com/docs/houdini19.0/hom/hou/SceneViewer.html#qtWindow [www.sidefx.com]
animatrix_
mabelzile
FYI, hou.SceneViewer.qtWindow() was added in 19.0
https://www.sidefx.com/docs/houdini19.0/hom/hou/SceneViewer.html#qtWindow [www.sidefx.com]

I use this a lot also. I hope SESI will add anypane.qtHandle() in the future. Ideally the entire GUI should be Qt based then you can do anything you want.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB