Saturday 28 October 2017

Maya TD tips - that awesome 'toolkit' you always wanted...

I got tired - tired of seeing icons on shelves in Maya disappear and noting that even when working, flipping to shelves to change the tool set that was being used meant the pipeline tools that I'd painstakingly developed would be hidden in a shelf that probably wouldn't be set back again when needed.

I thought to myself, wouldn't it be better to pop together a simple window with icon buttons on it and have it automatically launch when Maya started.  It would resolve the 'shelf switching' issue for a start, I was guaranteed to not 'lose' control buttons and I could design my own custom style interface for that professional coolness.   In fact, I could probably make it 'stick' to Maya, always be visible and look like it was just part of the whole application!  Now THAT would be cool...

What I came up with for my 3rd year 3D students for their current group production was a neat little side bar to make their pipeline cleaner and more streamlined.

Ooh!  What a pro, Kev!

This article is all about how you can approach building your own UI solution to make sure your team always have their tool set available.  But enough talking - let me run over the process.


It starts with a UI window

For my UI, I decided I'd like to have a slick vertical strip of buttons that I would use to call my other tool scripts.  I created a simple UI with a column layout to produce what looked like a vertical tool bar.  I set a nice margin in the column layout to push the contents in from the edges a little, and added in basic buttons.  I also set the background to something that would stand out - in this case, super-slick black.

I always find classes are, eh, classy for a lot of my script code.  So I've built the tool bar UI example here as a class, with each button click firing off a function.  It doesn't necessarily need to be a class, but why not.  Later on, I may find that I want to create a flexible 'custom window system' class to make building new UI's easier.  Hmmmm - now that's an idea...  In which case a class is a nice way to build from...  but maybe for now I'll just leave that for later.

import maya.cmds as cmds

class kevsToolBar:

    def __init__(self):
        self.winID = "KPTB"
        self.dCtrl = ""
        
    def toolBarButton01(self,*args):
        print "Button 01 was clicked"

    def toolBarButton02(self,*args):
        print "Button 02 was clicked"
            
    def toolBarButton03(self,*args):
        print "Button 03 was clicked"
    
    def UI(self):    
        if not cmds.window(self.winID, exists=True):
            windowID = cmds.window(self.winID, title="The Toolbar")
            cmds.columnLayout(co=["both",8])
            cmds.separator (style='none', height=4)
            cmds.button( l="Button 1",c=self.toolBarButton01,w=80)
            cmds.separator (style='none', height=4)
            cmds.button( l="Button 2",c=self.toolBarButton02,w=80)
            cmds.separator (style='none', height=4)
            cmds.button( l="Button 3",c=self.toolBarButton03,w=80)

            # Dock it on the right of Maya
            self.dCtrl = cmds.dockControl(l="Tool Bar", a='right', con=self.winID, bgc=[0,0,0], mov=False,fw=100, ret=False)
            
def setToolbox():
    ktb = kevsToolBar()
    ktb.UI()

Docking it

That code pretty much is it.  So, lets take a quick peek at the most important line in here and let me explain what is happening...

# Dock it on the right of Maya
self.dCtrl = cmds.dockControl(l="Tool Bar", a='right', con=self.winID, bgc=[0,0,0], mov=False,fw=100, ret=False)

To make an interface attach to the side of Maya, that's just a case of using the dockControl() function instead of the common showWindow() function used when we create pop-up windows.

One thing you can run into are those users who decide as cool as a tool might be, they prefer to close windows and keep the UI the way 'they like to work'.  This can (and does) eventually create headaches later downstream when one person decides to not stick to the rules.  The dockControl() has a very handy couple of parameters we can use to make sure that the tool bar stays put.

The parameter mov=False is what prevents the tool bar being undocked/closed.  fw=100 defines the width the bar will be (100 pixels).  ret=False is used to define if the window contents are retained in memory if the dock is closed (somehow).  Making this false ensures if it does get closed, its removed cleanly.

We'll need this - for later...

Note that I also defined a simple function that will be called to open up the tool bar.  This function I added as its what Maya will need to call when it first starts up to initiate our tool bar.

def setToolbox():
    ktb = kevsToolBar()
    ktb.UI()

Run it.

If we run the code, it should compile without error.  Once done, you simply need to call the function that launches the tool bar called setToolbox()... Then - ta-daaaa - a vertical tool bar on the right side of Maya should appear.  Plus, if we resize the main Maya window, our dock stays attached - maintaining its 100 pixel width at the same time.

With the tool bar locked on,  the tools stay and ensure artists can always access them.




Great - once we change out the print statements in our functions with calls to our scripts, our tool bar is ready and the job is done...  Well, almost!

Auto-launching the tool bar

As some of you (and probably many others may not) know, we can have Maya run scripts on start up by creating a python script called userSetup.py

When Maya starts, it will look for this script - we can make use of this opportunity to check files, set project folders, or in this case launch and set up the tool bar.

But - one problem.  userSetup is called BEFORE the Maya UI is started, and just inserting it into the script won't necessarily work...  Luckily though, Maya has a function called evalDeferred() which will tell Maya to run a command after the UI has started.

Setting our tool bar script to launch is as simple as placing this into the userSetup.py file as follows.  Note that in this example, the toolbar script was saved as myToolBar.py - making sure to keep an eye on the case used (ie. myToolbar is different than myToolBar when it comes to importing a module. If you stick to using camelCase then you can minimise small typo issues like this).

We need to import this, then call the function using the executeDeferred() function as shown below...  Here's my userSetup.py script.

# Launch UI script
from maya.utils import executeDeferred
import myToolBar
executeDeferred("myToolBar.setToolbox()")

Testing

Once we're done - close Maya (if its running).  Restart Maya and watch...  If it worked, then after Maya kicks in, we should see our tool bar magically appear, just like it was part of the UI itself!  Yus!  If you've gotten to here - you're now set to build your awesome new TD tool kit for your production team!

Slot in your tools, your scripts and go to town on making something pro!

Uh-oh!  What if it doesn't work

As with all coding, there are bound to be the occasional syntactical error pop up and cause issues. What you won't see when you start the script this way are python error messages.  After finding it somewhat infuriating to never see the tool bar appear, it became very clear that any time this wasn't working, it was down to sometimes the most simple python errors!  Here's a few bullet points to help.

(A)  Firstly - check your userSetup script for typo's or errors, including the aforementioned imported module filenames.

(B)  If it looks good - check the tool bar script itself.  If that looks good - you'll need to check any other script you've imported or called to see which one had an issue.

(C)  Likewise, check any third party dependencies (one example was the installation of the requests module needed to run a Trello task management script was not installed on the machine that the scripts failed on).

Here's a simple tip if you're getting frustrated : I found copy-pasting or loading any of the python scripts that are related to your pipeline system into Maya's script editor and running it will show up any errors.  That includes the userSetup script too - it only takes one indent issue, typo or bad syntax error to just stop the whole system.  Let Maya complain and tell you what it is...

All good

Once you've resolved issues, got your script to automatically pop up on start up - well done!  You're now qualified to impress your team with your amazing custom tool kit!

TIP : Replacing the buttons with icons

Icon's can look oh-so-much-sexier than text buttons.  I designed a collection of these and replaced by button() commands with symbolButton() instead.  This type of control let us use images as buttons.  Nice, and much cooler of course.

When using images for buttons, make sure to 'install' them into the documents / Maya / 2017 / prefs / icons folder.  The original code example earlier I used text buttons.  In the example below, I show how I would replace Button 1 with a cool icon called coolIcon01.png

cmds.symbolButton( image='coolIcon01.png', c=self.toolBarButton01, w=80)

Done!

Hope that this was useful - and have loads of fun playing with it (or just fun making awesome icons - even more fun then coding!)

1 comment :