JokerMartini
JokerMartini

Reputation: 6147

python + maya ui? Button behind others

I'm not quite sure where I'm going wrong here. I'm new to the maya scene in terms of python GUI making. What is messed up in my code? I want the bottom button to span the entire width of the dialog. I thought the layout was correct but when you run the code the button seems to be getting stuck behind some column.

'''
John Martini
Quad Sphere 1.0

More info on how to use it can be found at 
http://JokerMartini.com
'''

import maya.cmds as cmds
import random

class jmQuadSphere(object):

    def __init__(self):
        pass

    def show(self):
        self.GUI()

    def GUI(self):
        #check to see if our window exists
        window = "jmQuadSphere"
        if cmds.window(window, exists=True):
            cmds.deleteUI(window)

        # create our window
        window = cmds.window("jmQuadSphere", title = "Quad Sphere", mnb = False, mxb = False, sizeable = False)

        cmds.setParent(window)
        # Row layout that specifies 3 columns and the width for each column.
        cmds.rowColumnLayout('rlOpts', nc=2, cw=[(1,90),(2,90)])
        # Each button after the row layout is automatically insterted into the columns numerically
        cmds.text( label='Radius:', align='left' )
        cmds.floatField('spRadius', minValue=0, maxValue=999999999, value=10, precision=3, step=.01 )

        cmds.text( label='Height:', align='left' )
        cmds.floatField('spHeight', minValue=0, maxValue=999999999, value=10, precision=3, step=.01 )

        cmds.text( label='Width:', align='left' )
        cmds.floatField('spWidth', minValue=0, maxValue=999999999, value=10, precision=3, step=.01 )

        cmds.text( label='Depth:', align='left' )
        cmds.floatField('spDepth', minValue=0, maxValue=999999999, value=10, precision=3, step=.01 )

        cmds.text( label='Height Segs:', align='left' )
        cmds.intField('spHeightSegs', minValue=0, maxValue=999999999, value=4 )

        cmds.text( label='Width Segs:', align='left' )
        cmds.intField('spWidthSegs', minValue=0, maxValue=999999999, value=4 )

        cmds.text( label='Depth Segs:', align='left' )
        cmds.intField('spDepthSegs', minValue=0, maxValue=999999999, value=4 )

        cmds.text( label='Roundness:', align='left' )
        cmds.floatField('spRoundness', minValue=0, maxValue=100, value=100, precision=2, step=.01 )

        cmds.text( label='Flatness:', align='left' )
        cmds.floatField('spFlatness', minValue=0, maxValue=100, value=0, precision=2, step=.01 )


        cmds.setParent(window)
        cmds.rowColumnLayout('rlRun', nc=1)

        cmds.button('btnCreateQuadSphere', l="Create", w=180, h=30)

        # show window
        cmds.showWindow(window)

#execute tool
jmQuadSphere().show()

cmds.scriptEditorInfo(ch=True)

Screenshot:

screenshot

Upvotes: 1

Views: 3369

Answers (2)

Patrick Woo
Patrick Woo

Reputation: 61

Maya's UI system is created with a set of logic behind it. It will work well in very controlled and predictable ways once you get used to the rationale behind the hierarchy of the layouts and controls.

I have worked with both PyQT (PySide) and Maya's UI framework, and I don't find Maya's to be clunky at all. In fact, Maya's UI system is more streamlined and fuss-free than PyQT. It takes lesser lines of code to get the same UI layout and functionality. Of course, the biggest advantage of QT is the cross-platform and cross-software independence. This alone is enough reason to pick it up.

If you don't intend for your code to be portable outside of Maya, the benefit of using Maya's UI is that the commands and thus your code will always be supported as far into the future as possible.

Take the recent Maya 2017 for example. From Maya 2016 onwards, the UI framework was switched to PyQT 5 and PySide 2. This will break the code written for PyQt4 and PySide 1. However, if you have written your UI with Maya's UI system, it will continue working fine. Of course, revising your PyQT based code to make the changes required to be compatible with the new framework could be as simple as a search and replace in the editor, but there will still be a chance that you could break something.

For this way of working with Maya's UI with columnLayout, rowLayout, you need to keep tabs (pardon the pun) on the level which you are creating your components.

rowColumnLayout is confusing because of the sheer amount of control over rows and columns all at once. I usually work with a main columnLayout (vertically going down the UI), and when I need multiple controls laid out horizontally, I will use the rowLayout just for that row.

setParent is your friend. Use setParent to put your UI creation 'cursor/pointer' back under the level of UI hierarchy you want.

In my first example code, I show how I use variables to store the names of layouts. This way I can easily jump between different levels in the UI hierarchy of components.

UI screenshot 1

Run my following block of code and see the flexibility we have with the layout. It is enough for what I need to do with UIs in Maya.

import maya.cmds as mc
def buildUI():
  winName = 'myWindow'
  winWidth = 200 # set a target width and reference this when you specify width
  if mc.window(winName, exists=True):
      mc.deleteUI(winName)
  mc.window(winName, width=winWidth, title='Test Window')
  # i always have keep a reference to the main columnLayout
  mainCL = mc.columnLayout() 
  mc.text(label='text row 1')

  # tmpRowWidth controls the width of my columns in the rowLayout
  # with reference to winWidth
  tmpRowWidth = [winWidth*0.3, winWidth*0.7]
  mc.rowLayout(numberOfColumns=2, columnWidth2=tmpRowWidth)
  # at this point our UI pointer is under the rowLayout
  mc.text(label='row column1', align='center', width=tmpRowWidth[0])
  mc.button(label='column 2', width=tmpRowWidth[1])
  # we've used up number of children components, if you add 1 more, 
  # Maya will throw an error
  # now to move our pointer back up the hierarchy, 
  # which is our main columnLayout, mainCL
  mc.setParent('..') # also can use mc.setParent(mainCL)

  tmpRowWidth = [winWidth*0.5, winWidth*0.5]
  mc.rowLayout(numberOfColumns=2, columnWidth2=tmpRowWidth)
  mc.text(label='row column1', align='center', width=tmpRowWidth[0])
  mc.button(label='column 2', width=tmpRowWidth[1])
  mc.setParent('..')

  tmpRowWidth = [winWidth*0.7, winWidth*0.3]
  mc.rowLayout(numberOfColumns=2, columnWidth2=tmpRowWidth)
  mc.text(label='row column1', align='center', width=tmpRowWidth[0])
  mc.button(label='column 2', width=tmpRowWidth[1])
  mc.setParent('..')

  mc.text(label='text row 3')

  tmpWidth = [winWidth*0.3, winWidth*0.5, winWidth*0.2]
  mc.textFieldButtonGrp(label='txt field', w=winWidth, columnWidth3=tmpWidth, buttonLabel='okay')
  mc.button('full width button', width=winWidth)

  mc.showWindow(winName)
  mc.window(winName, e=True, width=winWidth, height=1)
  return
buildUI()

With a little bit of change in code, I can achieve the following: UI Screenshot 2

The original layout is still in there, but now I've put it into a column of a larger rowLayout.

Here is the code that generated this.

import maya.cmds as mc
def buildUI():
  winName = 'myWindow'
  winWidth = 400 # set a target width and reference this when you specify width
  if mc.window(winName, exists=True):
      mc.deleteUI(winName)
  mc.window(winName, width=winWidth, title='Test Window')
  # i always have keep a reference to the main columnLayout
  mainCL = mc.columnLayout() 
  mainRLWidth = [winWidth*0.6, winWidth*0.4]
  mainRL = mc.rowLayout(w=winWidth, numberOfColumns=2, columnWidth2=mainRLWidth, rowAttach=(2, 'top', 0))
  mc.columnLayout(w=mainRLWidth[0]) # create a columnLayout under the first row of mainRL
  mc.text(label='mainRL column 1', font='boldLabelFont')
  mc.text(label='')
  mc.text(label='text row 1')

  # tmpRowWidth controls the width of my columns in the rowLayout
  # with reference to winWidth
  tmpRowWidth = [mainRLWidth[0]*0.3, mainRLWidth[0]*0.7]
  mc.rowLayout(numberOfColumns=2, columnWidth2=tmpRowWidth)
  # at this point our UI pointer is under the rowLayout
  mc.text(label='row column1', align='center', width=tmpRowWidth[0])
  mc.button(label='column 2', width=tmpRowWidth[1])
  # we've used up number of children components, if you add 1 more, 
  # Maya will throw an error
  # now to move our pointer back up the hierarchy, 
  # which is our main rowLayout, mainRL
  mc.setParent('..') # also can use mc.setParent(mainRL)

  tmpRowWidth = [mainRLWidth[0]*0.5, mainRLWidth[0]*0.5]
  mc.rowLayout(numberOfColumns=2, columnWidth2=tmpRowWidth)
  mc.text(label='row column1', align='center', width=tmpRowWidth[0])
  mc.button(label='column 2', width=tmpRowWidth[1])
  mc.setParent('..')

  tmpRowWidth = [mainRLWidth[0]*0.7, mainRLWidth[0]*0.3]
  mc.rowLayout(numberOfColumns=2, columnWidth2=tmpRowWidth)
  mc.text(label='row column1', align='center', width=tmpRowWidth[0])
  mc.button(label='column 2', width=tmpRowWidth[1])
  mc.setParent('..')

  mc.text(label='text row 3')
  tmpWidth = [mainRLWidth[0]*0.3, mainRLWidth[0]*0.5, mainRLWidth[0]*0.2]
  mc.textFieldButtonGrp(label='txt field', w=mainRLWidth[0], columnWidth3=tmpWidth, buttonLabel='okay')
  mc.button('full row width button', width=mainRLWidth[0])
  mc.setParent('..') # this will exit the rowLayout back to the mainRL, same as mc.setParent(mainRL)

  mc.columnLayout(width=mainRLWidth[1]) # start another vertical layout
  mc.text(label='mainRL column 2', font='boldLabelFont')
  mc.text(label='')
  mc.text(label='text row 1')
  mc.button(label='button', width=mainRLWidth[1]*0.95, height=70)

  mc.setParent(mainCL) # set UI pointer back under the main columnLayout
  mc.text(label='')
  mc.button(label='full window width button', width=winWidth, height=40)

  mc.showWindow(winName)
  mc.window(winName, e=True, width=winWidth, height=1)
  return
buildUI()

I hope this has changed your opinon of Maya's set of UI system. It still has a lot going for it.

Most important of all, Maya will keep supporting these commands across its versions as much as possible, and it will still look and behave similarly across Windows, MacOS and Linux.

Upvotes: 2

mhlester
mhlester

Reputation: 23251

Gawd I hate Maya's layouts. formLayout all the way! (If not QT)

It looks like the rowColumnLayout is the width of the first column, even though it's not a child of the first rowColumnLayout. You can explicitly set the width:

cmds.setParent(window)
cmds.rowColumnLayout('rlRun', nc=1)

cmds.button('btnCreateQuadSphere', l="Create", w=180, h=30)

Considering you only have one column, there's no reason to use rowColumnLayout when you can just use columnLayout:

cmds.columnLayout('rlRun', width=180)

Screenshot:

enter image description here

Upvotes: 2

Related Questions