Blog of Rob Galanakis (@robgalanakis)

Qt Designer is harmful, exhibit A

Last week, iker j. de los mozos posted a Qt tutorial on his blog. The post was retweeted a number of times, so I figure people liked it.

The post exemplifies what is wrong with the Qt Designer, and also how a little more investment in learning can pay dividends for your career.

I know it’s unfair to give people code reviews on things they just put out for free, but I consider it even worse to allow people to continue to use the Qt Designer with a clear conscience. I thank Ike for his post, and for syndicating his feed on Planet Tech Art, and hope that no one takes my writeup below personally. It’s not an attack on a person, it’s trying to point out there there is a much better way to do things.

There are 117 lines of Python code in Ike’s tool for locking and unlocking transformation attributes. This sounds like a small amount, but is for an experienced Pythonista it indicates huge waste. For comparison, the entire ZMQ-based request/reply client and server I built for Practical Maya Programming with Python is the same size or smaller (depending on the version). If we take a closer look at his code (see link above), we can see a ton of copy and pasted functionality. This is technically a separate concern from the use of the Designer, but in my experience the two go hand-in-hand. The duplication inherent in GUI tools carries over to the way you program.

Let’s look at some refactored code where we build the GUI in code (warning, I haven’t tried this code since I don’t have Maya on this machine):

from functools import partial
from PySide import QtGui
import pymel.core as pmc
import qthelpers

OPTIONS = [
    dict(label='Position', btn='POS', attr='translate'),
    dict(label='Rotation', btn='ROT', attr='rotate'),
    dict(label='Scale', btn='SCALE', attr='scale')
]

def setLock(obj, attr, value):
    obj.setAttr(attr, lock=value, keyable=not value)

def isAttrLocked(obj, attr):
    return obj.getAttr(attr, q=True, lock=True)

def toggleRowCallback(attr):
    obj = pmc.ls()[0]
    value = isAttrLocked(obj, attr + 'X')
    for axis in 'XYZ':
        setLock(obj, attr + axis, value)

def toggleCellCallback(attr, state):
    obj = pmc.ls()[0]
    setLock(obj, attr, state)

def makeRow(options):
    return qthelpers.row(
        [QtGui.QLabel(options['label'])] +
        map(lambda axis: qhelpers.checkbox(onCheck=partial(toggleCellCallback, options['attr'] + axis)), 'XYZ') +
        qhelpers.button(label='lock ' + options['btn'], onClick=partial(toggleRowCallback, options['attr']))
    )

def main():
    window = qthelpers.table(map(makeRow, OPTIONS), title='lockAndHide UI', base=QtGui.QMainWindow)
    window.show()

Why is this code better? Well, for starters, it’s less than a third of the size (37 lines) and there’s less duplication. These are very good things. When we want to change behavior- such as auto-updating the checkboxes when our selection changes- we can put it in one place, not nine or more.

So the code is better, but what other benefits are there to not using the Designer?
– We pull common primitives, like a “row” (QWidget with HBoxLayout) and “table” into a qthelpers module, so we can use this across all GUIs. This saves huge amounts of boilerplate over the long run, especially since we can customize what parameters we pass to it (like onClick being a callback).
– The GUI is clear from the code because the UI is built declaratively. I do not even need to load the UI into the Designer or run the code to understand it. I can just read the bottom few lines of the file and know what this looks like.
– You learn new things. We use functools.partial for currying, instead of explicit callbacks. This is more complicated to someone that only knows simple tools, but becomes an indispensable tool as you get more advanced. We are not programming in Turtle Graphics. We are using the wonderful language of Python. We should take advantage of that.

Again, I thank Ike for his blog post, and hope I haven’t upset anyone. Ike’s code is pretty consistent with the type of code I’ve seen from Technical Artists. It’s time to do better. Start by ditching the Designer and focusing on clean code.

5 thoughts on “Qt Designer is harmful, exhibit A

  1. BrianH says:

    Hi Rob – Is the qthelpers module your own module, or is it sourced from another location? It seems from how you mention it that it is internally created, but I see both Spyder and GUIData have qthelpers modules. In looking at them, I guess it’s the same – they are just using these as convenience modules to pass Qt primitives?

    1. Hi Brian. The `qthelpers` module is just a stand-in for a common library/set of helpers. It doesn’t actually exist. But I, and like you have seen, many other people/projects, have built their own sort of thing. So you are totally right: they are just higher-level wrappers over Qt primitives that are specific to a project.

  2. Ike says:

    Hey, Rob! Thanks a lot for reviewing my code and taking it not one step, but hundreds, further. Although the small tools that I get to create for my day-by-day get the job done, they lack the elegance and scalability of more elaborated code. Less copy/paste is, definitely, always better.

    So I owe you one!!!

    1. Thanks for being such a good sport Ike!

  3. Chris Dardis says:

    I read Ike’s post a while back. Having just freshly been reading Rob’s book including the part where he eschews the QT designer, my immediate thought was “I wonder what Galanakis thinks?” :). Now we know! Great example, explained well!

    Points to Ike as well, to this day my knees tremble when I share my code with impassioned/knowledgeable pythonistas ;)! Keep sharing mate, it helps a lot!

Leave a Reply