Little help with maya commands & python

Hello everyone,
I started learning python/mel for maya and I need a little help with my script.

I am trying to make a tool that will check the mesh and delete those who share the same world position, the same number of vertex and the same bounding box.

Here is the link to my script : http://bit.ly/2Vucv9k

the “import myMath” cointains 1 function that returns the distance between 2 points in the world
it’s just something like :
sqrt( (x1 - x2)² + (y1 - y2)² + (z1 - z2)²)
the “import windowError” show a window dialog with a simple text

the script works but I think the way I did my progress bar is weird and honestly I think there is a better way to do it but I don’t know how.
You are welcome to tell me anything that may be wrong in my script (and I am sure there are a lot of things that must be fixed :smiley: ) .

Thank you for your time :slight_smile:

I’ve only got a couple things to say.
First, THANK YOU for using the whole word for the keyword arguments! Please don’t ever stop.

It may be more convenient in the future to not have to unpack all those values when finding the distance between 2 points. So instead of def distance(x1, x2, y1, y2, z1, z2):
I would do something like def distance(point1, point2):. However, this is not needed at all, and is just my own personal preference.

Oh, and you shouldn’t ever use a mutable object as a default. You’ve got
def removeDuplicate(maxDistance = 0.1, allObjectsList = []):
It should be allObjectsList=None in the function definition
And then check if allObjectsList is None: allObjectsList = [] later in the function
The reason is changing that argument actually changes the default.

def myAppender(myVal, myArg=[]):
    myArg.append(myVal)
    return myArg

print myAppender('T') # prints ['T']
print myAppender('A') # prints ['T', 'A']
print myAppender('O') # prints ['T', 'A', 'O']

As for the progress bar, I don’t know my way around maya UI commands, sorry.
But other than that, LGTM.


As a small aside, does anybody know why we use import maya.cmds as cmds instead of from maya import cmds?

I can answer that one, its an older idiom that persists in a lot of maya python examples and tutorials.

As for the progress bar stuff, I can’t remember exactly how those work anymore, but I’ll try to poke around tomorrow.

Thank you for your answers.

I mostly use whole words for the keyword arguments because it is easier for me to understand what I am changing. But I guess I’ll continue because you are not the only one who told me not to stop ^^

Hmm… I forgot that I could put “none” for a default.
I’ll change it later, thanks

For the “import maya.cmds as cmds” and the “from maya import cmds”

Which one would be the right one to use ?
I have seen both in 2 differents tutorial but I decided to use “import maya.cmds as cmds” because it seems more coherent to only use “import x”

They are both valid, and accomplish the same thing. It was just something I noticed, and didn’t have an answer for.

As a style thing, I’d suggest breaking the big main function up a bit. For example you’re doing this inline

BBObj = cmds.polyEvaluate(allObjectsList[obj], boundingBox = True)
                BBObj2 = cmds.polyEvaluate(allObjectsList[obj2], boundingBox = True)
        
                BBObjMin = (BBObj[0][0], BBObj[1][0], BBObj[2][0])
                BBObjMax = (BBObj[0][1], BBObj[1][1], BBObj[2][1])
                distanceMin = myMath.distance(BBObj[0][0], BBObj2[0][0], BBObj[1][0], BBObj2[1][0], BBObj[2][0], BBObj2[2][0])

                BBObj2Min = (BBObj2[0][0], BBObj2[1][0], BBObj2[2][0])
                BBObj2Max = (BBObj2[0][1], BBObj2[1][1], BBObj2[2][1])
                distanceMax = myMath.distance(BBObj[0][1], BBObj2[0][1], BBObj[1][1], BBObj2[1][1], BBObj[2][1], BBObj2[2][1])

You could convert that to a separate function like compare_bounds() and move it outside of the loop, then just call it. That helps reusability and also readability.

On a related note it’s usually a good idea to break up index-access blocks (like the BBObj2[0][1], BBObj2[1][1], BBObj2[2][1]) into something more legible. For example

    def bounds_from_object(obj):
          xcoords, ycoords, zcoords = cmds.polyEvaluate(obj, boundingBox = True)
          min_bbox = xcoords[0], ycoords[0], zcoords[0]
          max_bbox = xcoords[1], ycoords[1], zcoords[1]

         return min_bbox, max_bbox

extracts the min and max corners of the box just as you are doing, but it’s easier to spot an issue and see a problem (whereas with all indices it’s harder to notice a bug like

       BBObj2Min = (BBObj2[0][0], BBObj2[0][0], BBObj2[2][0])

which accidentally uses the minimum X twice instead

It’s a good idea to think about how you’re structuring your loops. You don’t need to do indexes like you would in, say, c# ; instead you can just loop over lists directly:

        delenda = []
        for first_object in all_object_list:
             for second_object in all_object_list:
                   if first_object == second_object:
                        continue
                   
                   dist_min, dist_max = compare_bounds(first_object, second_object)
                   if distanceMin <= maxDistance and distanceMax <= maxDistance:
                         delenda.append(second_object)

Which raises a point about the algorithm: I think that as written you’re checking each pair of objects twice, since every item is checked against every other in the second loop, meaning you’ll try both A->B and B->A.

There are a couple of ways around that; the itertools module has a function that produces all the combinations in a list itertools.combinations(all_object_list, 2) would give you all the unique pairs of objects in all_object_list. Or, you could track which pairs have already been tried by using sets:

        delenda = []
        seen = []
        for first_object in all_object_list:
             for second_object in all_object_list:
                   if first_object == second_object:
                        continue
                   if set([first_object, second_object]) in seen:
                       continue
                   else:
                       seen.append(set([first_object, second_object]))
                   dist_min, dist_max = compare_bounds(first_object, second_object)
                   if distanceMin <= maxDistance and distanceMax <= maxDistance:
                         delenda.append(second_object)
1 Like

Keberos got that one mostly right. The obj + 1 in the inner loop keeps that from happening

    for obj in range(len(allObjectsList)):        
        for obj2 in range(obj + 1, len(allObjectsList)):

But now that you mention it, I would probably only loop to the second-to-last item in the outer loop as I show below. The original way still works because the range in the inner loop would be empty for the last outer loop, but “Explicit is better than implicit”, so I would still make the change.

    for obj in range(len(allObjectsList) - 1):
        for obj2 in range(obj + 1, len(allObjectsList)):

Oh and Keberos, In case you don’t know why I quoted “Explicit is better than implicit”. Go to a python interpreter and run import this, and it prints out a paragraph called “The Zen of Python”, which is just a list of loose guidelines and ideals that the python community likes. You can take it or leave it. But I like it, and “Explicit is better than implicit” is one of those.

As a style thing, I’d suggest breaking the big main function up a bit. For example you’re doing this inline

I thought about it but my mind was on another thing (like the progressBar).
It’s true though that the readability of this part isn’t perfect. Even I have a hard time reading it :smiley:

There are a couple of ways around that; the itertools module has a function that produces all the combinations in a list itertools.combinations(all_object_list, 2) would give you all the unique pairs of objects in all_object_list .

I read about these but I wanted to have more experience with the basic and I didn’t want to import other module.
This is why I created this:

sqrt( (x1 - x2)² + (y1 - y2)² + (z1 - z2)²)

and didn’t import the math module which would have help me get the distance between 2 points in space.

Or, you could track which pairs have already been tried by using sets: didn’t know about sets.

I didn’t know about set. I’ll have to look it up, Thanks

Oh and Keberos, In case you don’t know why I quoted “Explicit is better than implicit”. Go to a python interpreter and run import this, and it prints out a paragraph called “The Zen of Python”, which is just a list of loose guidelines and ideals that the python community likes. You can take it or leave it. But I like it, and “Explicit is better than implicit” is one of those.

Another things I’ll have to look it up :smiley:

Every tutorials (and I guess everyone in general) tell that it’s good to write code that can be reuse in the future.
But how much do you reuse your code ? And how do you reuse it ?
you call the function directly and copy/paste it ?

Is there an easy way do make my code run faster ?
I made another script who check if a face has another face on top of it. (just like my script here that removes duplicated mesh)
But it’s really slow. Even when there are no more than 1000 faces.

I surely is on another level of skills that I don’t have right now but I was wondering if there was a method I didn’t know who could help me.

Thank you for your answers !

I reuse code pretty constantly.

The idea that I like to follow is that you’ve got 2 types of code, you’ve got library code, and you’ve got tool code.

Library code is stuff that you use all over the place, tool code is stuff that is used for a specific tool, and isn’t intended to be used elsewhere.

Basically for library code you write all of your functions / classes in a module and then in your tool’s code you’d do:

from my_libs.math.helpers import distance, square_distance
dist = dist(pnt1, pnt2)

A good rule of thumb is that most UI related code is probably going to end up as tool code.
You might have a generic base dialog that you build everything on top of that comes out of a shared library, but each specific UI is generally pretty uniquely built for the purpose at hand.

Also one way to identify when code is better suited as library code is simply checking with yourself during each tool “am I writing something I’ve done before?” if yes, go back and refactor the original into some shared library, and now you’ll never have to write that again. (baring bugs and/or slight tweaks when you realize that get_selected_objects doesn’t always mean ALL the selected objects, because nothing is ever simple)

1 Like

I see. It’s a bit clearer now. Thank you agin bob.w
I think with more practice and I’ll understand better how much I can reuse my code and how to organize it.

Regarding UI, all the UI I have done are really simple and use maya commands.
It is basic but it works.
The script I have in mind would be more complicated and require something more dynamic. Something like a grid that has a lot of different background color.
But I think that would be impossible with the maya commands UI and I would need to learn a UI module like Qt.
Do you think learning Qt now is something I should do ? Or am I missing other module or maybe I am skipping steps ?
I don’t want to skip something important but it’s also important to work on something I find fun ^^

My personal bias is that it’s better to invest the time in learning maya and to keep the UI simple and functional until you really need more. the built in maya.cmds UI is very basic but it’s enough for most cases; if you’re still learning it’s probably better to learn the Maya side first before tackling an entire big new API.

Complete agree.

One thing to keep in mind is that the majority of the tools that ship with Maya are written with mel. And yes, the whole reason we exist as a profession is because often those tools are lacking, but on the other hand a whole lot can get done with that very simple toolkit.

Start small and simple if you can, and don’t stress yourself on making your first couple of tools super generic and extensible. In a few months you’ll probably realize better ways to build them, and a few months after that even better ways. Such is the process of learning.

1 Like

Thank you all for your advice. I’ll keep that in mind !

To get back on my first question :
Anyone knows how to connect correctly a progressBar ? :slight_smile:

Hello again :slightly_smiling_face:

I kept myself busy these days by creating new scripts.
I am trying to create an atlas generator.
I created a version where all the textures are baked with the same ratio.
This version works well.

And now I am trying to create an atlas generator who can accept 3 differents ratio.
And…It’s hard. I think I can see the end of this script but I have an error and I don’t know what it means or how to correct it.
The error :

# Error: TypeError: file <string> line 2: Object [[u'pCube44', u'pCube40', u'pCube41', u'pCube42'], [u'pCube43', u'pCube35', u'pCube34', u'pCube37'], [u'pCube36', u'pCube31', u'pCube30', u'pCube33'], [u'pCube32', u'pCube39', u'pCube38', u'pCube13']] is invalid #   

I am using the u3dlayout to create the atlas by only changing the scale of the object’s UVs and keeping a power of 2 ratio of each texture on the generated one.

How can I fix this error ?
Thank you

Can we get the full traceback? Make sure in the script editor “History” menu, that “Show Stack Trace” is checked. Then send the whole traceback that starts with the line Traceback (most recent call last):

That said, What I’m guessing you want to pass to the function is a flat list-of-strings. What you’re passing is a nested list-of-lists-of-strings There’s a couple ways to deal with that.


The way I’d probably do it:
You’re probably .append()-ing to that list somewhere. You should .extend() instead.


Another way:
You could flatten that list after the fact by using itertools.chain()
https://docs.python.org/2/library/itertools.html#itertools.chain

import itertools
flatList = list(itertools.chain.from_iterable(nestedList))