Search code examples
layoutmayapymel

PyMEL embedding layouts


what's the best way to embed multiple PyMEL layouts in a single form?

For example, for each row of a form, I want to specify that:

  • row one uses a 2-column layout for label+textField pair
  • row two uses a 1-column layout for a menu item that has its own annotation label
  • row three adds a full-width execution button to the 1-column layout
  • all controls scale appropriately if the window is resized by a user

TIA!

import pymel.core as pm

def test(*args):
    print('model name: {}'.format(modelTField.getText()))
    print('model geom: {}'.format(modelMenu.getValue())) 

if pm.window("testUI", ex=1): pm.deleteUI("testUI")
window = pm.window("testUI", t="Test v0.1", w=500, h=200)
mainLayout = pm.verticalLayout() 

# two column layout
col2Layout = pm.horizontalLayout(ratios=[1, 2], spacing=10)
pm.text(label='Model name')

global modelTField
modelTField = pm.textField()

col2Layout.redistribute()

# single column layout
col1Layout = pm.horizontalLayout()

global modelMenu
modelMenuName = "modelMenu"
modelMenuLabel = "Model mesh"
modelMenuAnnotation = "Select which geo corresponds to the model shape"
modelMenu = pm.optionMenu(modelMenuName, l=modelMenuLabel, h=20, ann=modelMenuAnnotation)
pm.menuItem(l="FooShape")
pm.menuItem(l="BarShape") 

# execute
buttonLabel = "[DOIT]"
button = pm.button(l=buttonLabel, c=test)

col2Layout.redistribute()

# display window
pm.showWindow(window)

Solution

  • Since the pm.horizontalLayout() and pm.verticalLayout automatically redistribute the content, you do not need to call the redistribute yourself. But for these functions to work you need a with statement like this:

    import pymel.core as pm
    
    def test(*args):
        print('model name: {}'.format(modelTField.getText()))
        print('model geom: {}'.format(modelMenu.getValue())) 
    
    if pm.window("testUI", ex=1): pm.deleteUI("testUI")
    window = pm.window("testUI", t="Test v0.1", w=500, h=200)
    
    with pm.verticalLayout() as mainLayout:
        with pm.horizontalLayout(ratios=[1, 2], spacing=10) as col2Layout:
            pm.text(label='Model name'
            global modelTField
            modelTField = pm.textField()
    
        with pm.horizontalLayout() as col1Layout:        
            global modelMenu
            modelMenuName = "modelMenu"
            modelMenuLabel = "Model mesh"
            modelMenuAnnotation = "Select which geo corresponds to the model shape"
            modelMenu = pm.optionMenu(modelMenuName, l=modelMenuLabel, h=20, ann=modelMenuAnnotation)
            pm.menuItem(l="FooShape")
            pm.menuItem(l="BarShape") 
        
            # execute
            buttonLabel = "[DOIT]"
            button = pm.button(l=buttonLabel, c=test)
    
    # display window
    pm.showWindow(window)
    

    To create UI with python it helps a lot if you enclose everything into a class, especially if you use PyMel. This way you could do:

    class MyWindow(pm.ui.Window):
        def __init(self):
            self.title = "TestUI"
        def buttonCallback(self, *args):
            do something....
    

    This is very helpful if you have callbacks since everything you need can be accessed from within the class and you need no global variables at all.