SceneViewer as QWidget in H18

   4879   15   6
User Avatar
Member
538 posts
Joined: 12月 2006
Offline
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
https://gumroad.com/alexeyvanzhula [gumroad.com]
User Avatar
Member
538 posts
Joined: 12月 2006
Offline
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

Edited by Alexey Vanzhula - 2019年12月9日 23:39:23

Attachments:
H18.png (10.9 MB)

https://gumroad.com/alexeyvanzhula [gumroad.com]
User Avatar
Member
1 posts
Joined: 4月 2017
Offline
What happens if you cast the widget pointer to int instead of long?
User Avatar
Member
538 posts
Joined: 12月 2006
Offline
Stackenblocken-Sidefx
What happens if you cast the widget pointer to int instead of long?
same result
https://gumroad.com/alexeyvanzhula [gumroad.com]
User Avatar
Member
387 posts
Joined: 11月 2008
Offline
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())
User Avatar
Member
538 posts
Joined: 12月 2006
Offline
Amazing! I'll try it a bit later. Thank you so much!
https://gumroad.com/alexeyvanzhula [gumroad.com]
User Avatar
Member
538 posts
Joined: 12月 2006
Offline
@pezetko

Great! Even my function works. All I need is to declare main_widget as global variable. Thank you!
Edited by Alexey Vanzhula - 2019年12月11日 07:38:22
https://gumroad.com/alexeyvanzhula [gumroad.com]
User Avatar
Member
4595 posts
Joined: 2月 2012
Offline
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?
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]

youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
User Avatar
Member
4595 posts
Joined: 2月 2012
Offline
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.
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]

youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
User Avatar
Member
22 posts
Joined: 6月 2017
Offline
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.
User Avatar
Member
538 posts
Joined: 12月 2006
Offline
H17.5:
from hutil.Qt import shiboken2

H18.0:
import shiboken2
https://gumroad.com/alexeyvanzhula [gumroad.com]
User Avatar
Member
4595 posts
Joined: 2月 2012
Offline
Thanks a lot guys, shiboken2 is a very strange module name but it works
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]

youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
User Avatar
Member
66 posts
Joined: 3月 2017
Offline
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
VFX Supervisor @ MPC London
User Avatar
Member
4595 posts
Joined: 2月 2012
Offline
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.
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]

youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
User Avatar
スタッフ
426 posts
Joined: 2月 2018
Offline
FYI, hou.SceneViewer.qtWindow() was added in 19.0
https://www.sidefx.com/docs/houdini19.0/hom/hou/SceneViewer.html#qtWindow [www.sidefx.com]
Edited by mabelzile - 2022年12月16日 09:13:02
User Avatar
Member
4595 posts
Joined: 2月 2012
Offline
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.
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]

youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
  • Quick Links