Building AETemplates with PyQt

I’ll be honest. Building an AETemplate using MEL is an activity that I enjoy about as much as going to the DMV. It’s very tedious and can take forever to get anything done. The more complexity you add, the more daunting the logistics become.

At this point, you’re probably thinking “Oh, he’s about to talk about how much better it must be to use PyQt!”

No, he isn’t.

While making AETemplates with PyQt has substantial advantages, which is why I do it, the overall process can be equally painful (just in different ways).

So why use PyQt? I was going to make a list but it really comes down to one thing: you get absolute control over all aspects of the interface including:

  • Layout (esp, the ability to stretch multiple widgets in a row or column)
  • Widget appearance (length, height, spacing)
  • The ability to create all new widgets to better visualize certain attributes. For instance, if your node had an attribute that required a user to enter a time in hours and minutes (for some odd reason), you could create a clock widget with hour and minute hands.


Look at that…made a list anyway.

The Disclaimer

Unfortunately, with great power comes a great repercussion:

See everything in that context menu? When you created that control with editorTemplate in MEL, you got all that for free. Want the background to turn red when you set a key? editorTemplate did that for you too. With PyQt, on the other hand, you’re going to need to build in that functionality yourself!

But really

Actually, the situation isn’t as dire as all that. Yes, there can be a high initial cost for building an AETemplate in PyQt but, if you’re smart and abstract as much as possible, you’ll end up with a library of standard base classes, widgets, and menus that you can go back to whenever a new AETemplate is needed.

However, it’s a lot of material to cover so we will save all the context menu related stuff for a later tutorial.

Tutorial outline

We will be working with the same uselessTransform node as in the previous post. The source code for that plus the MEL and Python code discussed in this tutorial can be downloaded here: uselessTransform_PyQt_AETemplate.

In my opinion, the conceptual side of making AETemplates with PyQt is not too complex. However, the process is also not as linear as the material we discussed in previous posts. We can’t simply start building and hope for the best…rather, some planning is required. With that in mind, this tutorial can be broken into two parts:

  1. The boilerplate code for building and updating an AETemplate with PyQt. This code is generally consistent across all AETemplates built in this manner. I usually just copy/paste from older projects.
  2. The attribute and widget specific code. This refers to any UI elements that we must develop for displaying and editing attributes of the specific node type this AETemplate is being written for.

Unlike earlier tutorials, I am mostly unable to show snapshots of the intermediate stages. Rather, it’s one of those all or nothing cases where everything comes together at the end.

Boilerplate

Just as before, we must start with the AEuselessTransformTemplate.mel file. The big difference is that we will not be using it for any significant work. Rather, we will hook into our Python functions from there. To that end, we must also create an AEuselessTransformTemplate.py file.

Note: You can actually call the .py file whatever you want…I just name it that way for consistency and clarity.

MEL

Let’s implement the main AEuselessTransformTemplate procedure:

Notice:

  • The significant line here is:
  • The final argument of that call is an empty string. We don’t actually want to pass any attributes but we still need that argument so that we can get the name of the node.
  • We still include the calls to AEtransformMain and AEtransformNoScroll, which will ensure that we still have the standard transform node template after our PyQt.

Now, let’s take a look at those buildQT and updateQT procedures:

Notice:

  • The procedure accepts a single argument, which is the name of the node. That name will have a trailing dot (.), which we must remove before proceeding.
  • We import the python AETemplate module here.
  • We get the name of the parent layout and pass it to the python buildQT method, along with the name of the node

The only differences are:

  • We do not import the python module.
  • We call updateQT instead of buildQT.

Python

We’ll need to import the following modules:

The Python boilerplate requires three functions and one class:

  • getLayout (function) – Convert a layout name (as provided by MEL’s setParent -q command) to a QtGui.QLayout object that we can insert our PyQt work into.
  • buildQT (function) – Install the PyQt AETemplate into Maya’s AETemplate layout. This is responsible for initializing all the widgets.
  • updateQT (function) – Update the AETemplate with new node information.
  • AEuselessTransformTemplate (class) – The primary widget that will contain all the AETemplate widgets and data.

If we run the template right now, we’ll get:

Have we actually accomplished anything? This looks the same as having no AETemplate at all. Actually, we’ve made it possible to add any PyQt widgets we want above that Transform Attributes section. Let’s add a simple QLabel to our AETemplate to validate our efforts:

Result:

Specialty Widgets

Now that we’ve got the standard stuff out of the way, it’s time to discuss the specialized widgets that will display the info for particular attributes. Each widget needs to be able to do the following:

  • Display the current value of an attribute.
  • Allow the user to set a new value and have it be applied to the attribute.
  • Update itself when the attribute gets changed externally (e.g., via the setAttr command).
  • Change the node that it’s connected to.

Base

Although we have three different types of attributes to create widgets for, a clever developer will notice that most of the functionality is consistent across all of them. Therefore, we can create a base class to derive our widgets from.

All attribute widgets will need to:

  • Store the current node and attribute names.
  • Have a text label with the attribute’s display name.
  • Have a basic left to right layout.
  • Maintain a scriptJob that calls an appropriate update method when the attribute is modified by some external action.
  • Track whether the UI is in the process of updating. This is necessary so that the scriptJob doesn’t trigger a UI update when the UI, itself, is the cause of that update.

With that in mind, let’s initialize our attribute base class:

Next, we need to define two virtual methods. One for updating the UI when the attribute has been modified and one for updating the attribute when the UI has been modified.

We actually want the updatingGUI variable to be True while the updateGUI method is running. However, as we don’t want to implement that logic in each child class, we’ll wrap updateGUI in another method that will be responsible for calling it:

We also don’t want updateAttr to run if updateGUI is running so we’ll add another wrapper for that:

setNode and scriptJob

There’s only one more thing we need to implement for our base class: the setNode functionality that will:

  • Update self.node.
  • Update the UI.
  • Initialize or update a scriptJob that will call the callUpdateGUI method whenever the attribute is changed externally.
  • Kill any pre-existing scriptJobs.

Notice that we only make changes to the scriptJob if:

  • self.sj is empty.
  • The current scriptJob ID assigned to self.sj is no longer valid.
  • The new node being set is different from the previously assigned node. As explained in the last post, the Update functionality of an AETemplate can be called for various reasons including a simple refresh of the UI. Therefore, it is inefficient to update the scriptJob unless the node has actually changed.

The Numerical Attribute (numAttr)

Now that we’ve completed our Base class, let’s start deriving from it to define the widget that will track the numAttr on our uselessTransform.

Notice:

  • Our base class took care of most of the work. All we had to do was create the QLineEdit widget responsible for actually displaying information for a numerical attribute and add it to the layout.
  • We connected the editingFinished() signal from self.valLE to callUpdateAttr.
  • Our final step is to call setNode. This is necessary to initialize the scriptJob. However, as it also calls updateGUI, we had to have all our UI elements in place first.

All we need now is to re-implement updateGUI and updateAttr:

Easy peasy!

The String Attribute (stringAttr)

This widget is pretty similar to the previous one. Just minor tweaks:

  • Modified the update methods to accommodate getting and setting a string instead of a number.
  • No QDoubleValidator assigned to the QLineEdit.

The Enum Attribute (enumAttr)

Last, but not least, we must implement a widget to support choosing a value from an enumerated list of options. Again, the only changes from the previous two widgets is the stuff specific to an enumerated list:

  • Using a extra argument in the __init___ to get the list of enumerated values to display.
  • Using a QComboBox instead of a QLineEdit.
  • Connecting a different signal to callUpdateAttr.
  • Minor changes to the implementations of the update methods.

Putting It Together!

We’re almost done! All that remains is to add our new widgets into the main AEuselessTransformTemplate class. We do this in the usual way:

setNode

One more thing. We must make sure that the setNode method of each widget in the AETemplate gets called during update. We do this by simply adding those widgets to the setNode implementation within AEuselessTransformTemplate:

Result!

And that’s how you build an AETemplate with PyQt!




8 Thoughts on “Building AETemplates with PyQt”:

  1. PaulWinex

    Thanks for the information. But I know an easier way. Need to use pymel for templates. Accessing and updating attributes layoutu more convenient and easier. Try to do so. I can give a sample.

    Reply
    • ostrod

      Привет Paul!

      Pymel is a great way of creating AETemplates and a major step up from using MEL or Python MEL. However, it’s still wrapping MEL commands, which means you’re still limited to the GUI building blocks that Maya gives you.

      My point in providing this PyQt AETemplate tutorial was to show that you don’t have to be. The full range of Qt’s capabilities can be available to you, if you should need them. You can paint your own widgets, re-implement events, etc.

      The key is understanding that it’s just another tool in the toolbox and one that should be used sparingly and only when the desired effect can’t be accomplished with MEL (or pymel).

      Reply
        • ostrod

          Ah, I see what you mean now. Similar logic but no need for MEL. Very cool! Definitely cleaner.

          I’ll remember this for the next AETemplate (or tutorial) I write. The only mitigating factor is that you need to have pymel imported first, which I don’t think my facility does right now (would have to check).

          Anyway, you should definitely put up this info as a tutorial on your site if you haven’t already!

          Reply

Leave a Reply

  • (will not be published)