Creating a simple UI in Maya using PyQt

In this post, I’m going to cover the very basics of using PyQt in Maya. I’m working with Maya 2011 but this should be relevant for 2012 & 2013 as well. To benefit from this tutorial, you should already be reasonably familiar with Python, particularly with respect to Maya. You do not have to know anything about PyQt or Qt.

Our project will be a very simple UI that lets the user pick a Sphere, Cube, or Cylinder poly type, give it a name, and create it with the push of a button. We’ll also provide a little descriptive line so that the user knows what’s going on. Our end result will look like this:

IMPORTANT: The PyQt libraries do not come w/ a standard Maya install so you’ll have to handle that part yourself. If you need help, Nathan Horne has a great post about how to do that: Guide to PyQt in Maya.

Before we start, you should know the following:

  • PyQt is a Python wrapper around Qt, which is a framework originally developed by Nokia for building interfaces. Except where noted otherwise, I will use the terms Qt and PyQt interchangeably.
  • Starting w/ Maya 2011, the UI has been rewritten in Qt, which makes it very easy for third party developers such as myself to access various components of the Maya UI or to simply add on our own interfaces.

Some PyQt terminology:

  • widget – The core building block of any Qt UI. Think of it as a lego block. You can combine multiple widgets to create new, more powerful widgets. Examples of simple widgets include text boxes, check boxes, buttons, text labels, drop down menus, etc.
  • layout – How widgets are visibly organized for a user. The layout is an actual object available to the Qt developer for unlimited tweaking. As we’ll see in future posts, we can actually take advantage of this fact by hooking into existing Maya layouts to add our own widgets.
  • signal – The primary means of communication between various widgets. Typically, when a widget is triggered or modified in some way (whether programmatically or by the user), it emits a signal to notify other interested widgets, which may need to perform their own actions in response.

In this tutorial we will:

  • Create a simple dialog and make sure it’s connected to the main Maya window.
  • Populate our dialog w/ simple, built-in widgets, made available to us by PyQt.
  • Layout the widgets in a logical way.
  • Connect to the signals (generally referred to as Signals and Slots) emitted by our widgets so that we can react to user input.

Source Code

The final source code for this tutorial can be found here:
qtBasic.zip

 Creating a simple dialog

First thing’s first, let’s import all the modules we’ll need:

Any floating window or dialog we create will need to be parented to Maya’s main window. The following is a boilerplate method to do that:

To create a new dialog window, we need to subclass from Qt’s QDialog class. Technically, we can subclass from any Qt widget class but certain classes like QDialog and QMainWindow are specifically designed to be their own floating window UIs and so provide some extra features such as being able to set the window title.

So, our first step is:

Our first step is complete! If you want, you can look at the result by executing:

Unfortunately, the result won’t be very interesting:

Adding widgets

Let’s spice things up a bit by adding some widgets.

Notice:

  • Each new widget got assigned to an instance variable of BasicDialog.
  • Each new widget was parented to the instance of BasicDialog.
  • The QLineEdit, QPushButton, and QLabel widgets received additional arguments. These are examples of simple widgets that allow you to set their text on initialization. We’re actually going to hook up the QLabel (self.descLabel) to a slightly more complex algorithm later on.

So what’s our dialog look like now?

Well, phooey!

Setting up the layout

As I’m sure you’d noticed, our dialog didn’t look quite right after we added the widgets. This is because Qt doesn’t know what to do with them. To correct this problem, we need to assign a layout to our dialog and add all our widgets to it.

Let’s start with something simple:

Result:

  • IMPORTANT: The layout’s final argument was the BasicDialog instance. This tells the layout that it is the primary layout for this dialog.
  • The widgets were added in the same order that they appear in the UI.

Well, this is better but I’d really prefer that the combo box, line edit, and push button were all on the same line. To do this we have to create a new layout just for them and then add that layout to our primary layout. The nice thing about Qt is that you can nest as many layouts as you need to get the look you want. Let’s tweak our layout code a bit:


Now that’s more like it! Notice how only the primary layout gets passed self (i.e., the BasicDialog instance) as the final argument.

Adding content to the Combo Box and description Label

Right now, our combo box is empty. However, we want it to provide the user with one of three options: Sphere, Cube, or Cylinder. We can easily achieve this by:

We also want the description label (self.descLabel) to always show up-to-date information from the shapeTypeCB and nameLE widgets. We’re going to separate that functionality into its own method because, as we’ll see shortly, we’ll need to update that description anytime those other widgets get changed. So, let’s write a simple method that’ll set the correct description value.

We should call this method from __init__ as well to initialize it properly:

Now, when we launch our dialog we get:
.

Setting up Signals and Slots

This is coming along nicely! Let’s see what happens when we change the shape type value in our combo box from Sphere to Cube and update the name in our line edit from “newShape” to “newCubeShape”:

Well, that doesn’t seem right.

Every widget emits signals when it’s updated in some way. We need to connect to those signals so that we know when the user has changed the name or shape type. Let’s use the connect method to do just that.

The arguments passed to connect are the following (in order):

  • The widget emitting the signal.
  • The signal being emitted. Each widget is capable of emitting many different signals so we need to specify the one that gets emitted when the condition we’re interested in occurs. For instance, the signal for nameLE is “textChanged”, which as the name suggests is emitted whenever the text gets changed.
  • The function/method that gets called every time the signal is emitted. In this case, we’re providing our updateDescription method.

Know that:

  • Each widget emits its own set of unique signals.
  • The description of which signals are emitted by which widgets can be found in the PyQt Class Reference, which I will discuss at the end of this post.

Now, when we update the name and shape type, our description updates accordingly:

Button Button, who’s got the Button?

Almost done! We just need to hook up the Make Shape button and we’ll be good to go.

Let’s define a simple method that will use the values from our dialog to create the corresponding poly shape in Maya:

At this point, it should be obvious what we do next: hook up a signal from the button to makeShape!

Further Reading and Documention

Hopefully, this tutorial gave you a sense of how simple but powerful PyQt can be. We will continue to explore this in further tutorials.

I’d like to conclude by providing some additional resources for digging deeper into PyQt:

  • PyQt Class Reference – Comprehensive documentation on the available PyQt widgets (and other classes) including which signals they emit and under what circumstances. When developing, I’ve found this site to be an invaluable reference.
  • Rapid GUI Programming with Python and Qt – An excellent reference for working with PyQt. Provides many examples and covers most major topics well.



2 Thoughts on “Creating a simple UI in Maya using PyQt”:

Leave a Reply

  • (will not be published)