[MaxPlus] parent PySide Window to Max Window

Hey Guys we develop a MaxPlus/Pyside Window inside 3dsMax. But the Pyside window we create is always a seperate window,not parented to max, so it stays visible even if we minimize max for instance.

Now we tried different approaches to solve that. the best working solution was the following piece of code. It works almost perfect but if you move the window it is very bugy as you can see on the screenshot. do you know any solution for that problem?

thanks in advance
Florian


class OpenWindow(QtGui.QMainWindow):

    def __init__(self, parent=None):
        super(OpenWindow, self).__init__(parent)

        WId=self.winId()
        if type(WId).__name__ == 'PyCObject':
            from ctypes import pythonapi, c_void_p, py_object
            pythonapi.PyCObject_AsVoidPtr.restype  = c_void_p
            pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]

            WId = pythonapi.PyCObject_AsVoidPtr(WId)
        MaxPlus.Win32.Set3dsMaxAsParentWindow(WId)





Does the window work properly when it is not attached to max? The symptoms look like the top widget of your UI might be a QWidget instance. Do you have the same problem with a QDialog or a QMainWindow?

Yes it works perfetct without. It is a QMainWindow no? “class OpenWindow(QtGui.QMainWindow):”

This might help:
http://area.autodesk.com/blogs/chris/pyqt-ui-in-3ds-max-2014-extension

[QUOTE=Munkybutt;26206]This might help:
http://area.autodesk.com/blogs/chris/pyqt-ui-in-3ds-max-2014-extension[/QUOTE]

Sadly this does not work with PySide. And even with Qt you are very dependent on choosing correct versions and builds and switching binaries. It’s a hassle. For now we sorta worked around it awkwardly.

[QUOTE=flo.ei;26205]Yes it works perfetct without. It is a QMainWindow no? “class OpenWindow(QtGui.QMainWindow):”[/QUOTE]

Oh wow! I am totally blind! Sorry about that. Hopefully you’ll find an answer soon, I’d be curious to hear about it!

I have exactly same behavior. Not parented works but cumbersome, parented has lot of redraw problem. Even repainting the widget on move event doesn’t solve it. And nothing new on Autodesk side since max2014. I’ve totally stop maxplus development since such base bahavior are not solved yet. And the link, asking to compile a blur version of blurpython is just a confession …

We are working around this with some custom behaviour. The Problem with the normal ParentToMax window thing is, that it forces the PySide window to stay within the boundaries of the main window. Which is a BIG hassle on multi-monitor systems.
So we have our MacroButton set up to check if our tool is already open, and if it is, de-minimize it and told users to minimize it and have set it to be Always on top. At least i think this is the current state.

Cheers,
Thorsten

from PySide import QtGui
from ctypes import pythonapi, c_void_p, py_object

class Example(QtGui.QWidget):
    
    def __init__(self):
        super(Example, self).__init__()
        self.initUI()

    def moveEvent(self, event):
        self.update()
        

    def initUI(self):
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Icon')
        self.show()
        
def main():
    ex = Example()
    _GCProtector.widgets.append(ex)

    main_layout = QtGui.QVBoxLayout()
    label = QtGui.QLabel("I'm a layout.")
    main_layout.addWidget(label)
    cylinder_btn = QtGui.QPushButton("I do nothing.")
    main_layout.addWidget(cylinder_btn)
    ex.setLayout(main_layout)

    hwnd = ex.effectiveWinId()
    pythonapi.PyCObject_AsVoidPtr.restype  = c_void_p
    pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]
    ptr = pythonapi.PyCObject_AsVoidPtr(hwnd)
    MaxPlus.Win32.Set3dsMaxAsParentWindow(ptr)

if __name__ == '__main__':
    main()

Here’s a kind-of-a-hack version so at least the main UI portion of your application does not suffer from drawing artifacts when dragging over a viewport. I think this is just a rendering bug in how max handles it’s viewport rendering, since dragging a window around on top of the side bars causes no artifacts.

You’ll see I just added:

    def moveEvent(self, event):
        self.update()

to the class as an event listener. The window’s frame is still problematic (I can’t find any equivalent of updateGeometry() for frameGeometry).

As instinct-vfx said, you’d still be dealing with the restriction of the window being held within the Max frame bounds. Let’s hope future versions open up the ability to bind Max application events into MaxPlus (if that even makes sense).

Hi all: I’m relatively new to the Max Python API in general, but I have intermediate experience in Python in general (Maya/XSI). The solution here has really helped me out a lot, but I have a related question: I noticed that the PySide distribution in max doesn’t seem to have QtUiTools lib, which I kind of need to import UI files from Designer: I was trying to get this solution working with the QuiLoader so that I can quickly prototype UI designs in Designer and load them into max; is there any way I can still do this easily? Converting the UI files to py files still requires that I parent the widgets to a QMainWindow, and I haven’t been able to get that working either; do you guys just tend to hand code all your UIs manually for this sort of stuff?

I did look at the Blur extensions for 2014 which bundles the PyQt libs and everything thus making this much less of a headache (than it should be! :P), but I’m a little worried about the support that those tools will get in the future, and I’m on 2015 in any case.

Also, because I’m not that good at ctypes, I went ahead and tried to figure out what’s actually going on in the sample code posted; if anyone’s willing to walk me through and check if I’m misunderstanding anything that’s happening, I have a revised version of the code snippet here:

Any help/guidance here would be appreciated!

Hi all, I’ve been struggling with trying to get a simple UI from Designer to load in 3ds max using this parenting method, however, because the PySide distribtion in max 2015 is kind of incomplete (No access to QUiLoader), I’m just converting the files to py and loading them in that way; however I’m running into a bit of an issue: I can get the UI files to load in max, but I can’t seem to get focus on them, and I can’t click on any of the buttons/menus (even though when I hover my mouse over them, they do highlight)

I was wondering if anyone would be kind enough to help look over my sample code and if possible, point out what I’m doing wrong here, I’d really appreciate it:

Thanks!

EDIT: Oh, actually now I realize it’s a little more complicated than that; the UI actually loads fine, but works ONLY on the primary monitor; if I have max open on a secondary monitor, all the bugs start happening…plus any keyboard input I give becomes captured by 3ds max itself instead of the QWidget.

Does anyone actually have a good way of doing this that they’d like to share? The code snippet above actually suffers from the same issues I’m describing here (only works on primary monitor, keyboard inputs don’t work etc.)

Good News Guys, in Max 2016 SP1 they fixed it, here is a mail from autodesk dev support:

Attaches a given parentless QWidget by its Qt winId to the win32

3ds max main window.

def AttachQWidgetToMax( qwidget, isModelessDlg=True ):
‘’’ Attaches a given parentless QWidget by its Qt winId to
the win32 3ds max main window.
This is internally done by creating an in-between QWinWidget,
which will be child of the 3d max window, and a parenting of
the QWidget to the QWinWidget.
For the given QWidget the 3ds max keyboards accelerators will
be disabled when the widget gets the focus.
By setting isModelessDlg to true, 3ds max will properly
disable/enable the QWidget when another modal 3ds max dialog
pops up. ‘’’

Good News Guys, in Max 2016 SP1 they fixed it, here is a mail from autodesk dev support:

Attaches a given parentless QWidget by its Qt winId to the win32

3ds max main window.

def AttachQWidgetToMax( qwidget, isModelessDlg=True ):
‘’’ Attaches a given parentless QWidget by its Qt winId to
the win32 3ds max main window.
This is internally done by creating an in-between QWinWidget,
which will be child of the 3d max window, and a parenting of
the QWidget to the QWinWidget.
For the given QWidget the 3ds max keyboards accelerators will
be disabled when the widget gets the focus.
By setting isModelessDlg to true, 3ds max will properly
disable/enable the QWidget when another modal 3ds max dialog
pops up. ‘’’

For those looking for a solution prior to Max 2017

The point of letting the widget behave like another Max child window (concerning Z-ordering) is to set the owner of the widget, not the parent.

The magical thing to do is

SetWindowLongPtr(self.hwnd, GWL_HWNDPARENT, self.parent_hwnd)

where parent_hwnd is the 3dsMax main window HWND.

For full reference check maxparenting.py (and the example file) GitHub - alfalfasprossen/qtinwin: Place a PySide widget inside a native windows window

Hey alfalfasprossen, this is awesome.

Quick question, has this been tested in windows 7, 8 and 10? Just curious because last I was mucking with this type of stuff, some of this seemed to be working in 7 but seemed to have changed in windows 10.

I only tested it on Windows7. The msdn site doesn’t say anything about changes to the API for Windows 10 though. I guess somebody just has to try it :slight_smile:

There is also this curious command… don’t know if this does the trick!
MaxPlus.AttachQWidgetToMax()

hey guys, why not try : from PySide import shiboken
in 3dmax we can’t use : import shiboken
btw,above 3dmax 2018 , we can use : from PySide2 import shiboken2
good luck

It seems that 3dmax < 2017 doesn’t work