Using PySide2

Autodesk 3ds Max ships with a pre-built version of PySide 2.0 compatible with Python 2.7.12. This version includes all standard PySide modules.

Example PySide Script

The following simple example shows how to obtain a handle for the PySide application object and create a widget:

'''
    Demonstrates how to create a QWidget with PySide2 and attach it to the 3dsmax main window.
'''

from PySide2 import QtCore
from PySide2 import QtWidgets
import MaxPlus
import pymxs

class PyMaxDialog(QtWidgets.QDialog):
    def __init__(self, parent=MaxPlus.GetQMaxMainWindow()):
        super(PyMaxDialog, self).__init__(parent)
        self.setWindowTitle('Pyside Qt Window')
        main_layout = QtWidgets.QVBoxLayout()
        label = QtWidgets.QLabel("Click button to create a cylinder in the scene")
        main_layout.addWidget(label)
        
        edit_box = QtWidgets.QLineEdit("Cyl Name")
        main_layout.addWidget(edit_box)

        cylinder_btn = QtWidgets.QPushButton("Cylinder")
        cylinder_btn.clicked.connect(self.make_cylinder)
        main_layout.addWidget(cylinder_btn)
        
        self.setLayout(main_layout)
        self.resize(250, 100)           
        
    def make_cylinder(self):
        cyl_name = self.findChild(QtWidgets.QLineEdit).text()
        cyl = pymxs.runtime.Cylinder(radius=10, height=30, name=cyl_name)
        pymxs.runtime.redrawViews() 
        

def main():
    w = PyMaxDialog()
    w.show()

if __name__ == '__main__':
    main()

Notes:

Loading Qt UI Files

MaxPlus provides a facility for loading UI files created by Qt Designer: MaxPlus.LoadUiType(). This example looks for a ui file named test.ui located under scripts/Python/ui/ and loads it.

Note: If you use the Python global __file__ to find the target ui file, the global will only evaluate when using the Scripting > Run Script command. In this example, we use the MAXScript getDir() function, which will work in any context.

import MaxPlus, os
from PySide2 import QtWidgets
from PySide2 import QtCore
from pymxs import runtime as rt

def makeTeapot():
    rt.teapot()
    
class TestDialog(QtWidgets.QDialog):
    def __init__(self, ui_class, parent=MaxPlus.GetQMaxMainWindow()):
        QtWidgets.QDialog.__init__(self, parent)
        self.ui=ui_class()
        self.ui.setupUi(self)
        self.ui.pushButton.clicked.connect(makeTeapot)

def main():
    script_dir = rt.getDir(rt.name('scripts'))  
    ui_path = os.path.join(script_dir, 'Python', 'ui','test.ui')
    ui_class, base_class = MaxPlus.LoadUiType(ui_path)
    instance = TestDialog(ui_class)
    instance.show()


if __name__ == '__main__':
    main()