Custom axis orientation in degrees

243 views
Skip to first unread message

Peter Makal

unread,
Nov 26, 2013, 6:55:16 AM11/26/13
to python_in...@googlegroups.com

Hey guys! I have started to write my very first script in Python for Maya and it seems to work just fine but I want to improve it in a few ways.

So first I'm going to show and describe this script for you and then those things I want to change/add.

How it looks:
http://i.imgur.com/tRLRX7Y.png

Code:
http://pastebin.ubuntu.com/6478436/


What it does:

Main purpose of this tool/script is to be an alternative and more convenient way to set up "Custom axis orientation" which in Maya is represented in radians. So what actually this tool allows you to do is to set X, Y and Z orientation in degrees. Furthermore it gives you some additional options (as you can see in the screenshot above).

How I want to improve it:
1. Allow to set Custom axis orientation for Rotation tool.
This is probably the hardest thing (I assume). As you can see in the image above, "Rotation" option is currently greyed out. That’s because manipRotateContext doesn’t have "Custom axis orientation" mode (
http://download.autodesk.com/global/docs/maya2013/en_us/CommandsPython/show.html?manipRotateContext.html&cat=General). Is there a way to achieve this effect - rotate Rotation axis?

2. Refresh text (values) in frame "Current values in degrees" after clicking on "Apply" button.
When window is created, in frame "Current values in degrees" values of X, Y and Z are retrieved from three fields in "Custom axis orientation".  Then they are converted to degrees and displayed (lines 63-83 in the code linked above). But what I also want is to refresh them each time I click "Apply" button. Is there a way to achieve this?

3. Combine formatting of "width", "align" and "precision" in Python.
Lines 75-83 are showing Python .format method. Each value has 3 float digit precision (.3f) and in addition to that they are right-aligned with 10 value (>10). Ok, that’s good but I what also want is to have three "0" before dot. So I want apply [width] to it (
http://docs.python.org/2.6/library/string.html#format-specification-mini-language). But I don’t know how to mix [align], [width] and [.precision] together in one formatting {}.

4. Code improvements
Because it’s my first script/tool and because I have learning Python for only 3 month now – I don’t have too much experience in it – any advice about the code structure will be highly appreciated :*


PS. And as always sorry for my English.

Justin Israel

unread,
Nov 26, 2013, 4:42:19 PM11/26/13
to python_in...@googlegroups.com

2. Refresh text (values) in frame "Current values in degrees" after clicking on "Apply" button.
When window is created, in frame "Current values in degrees" values of X, Y and Z are retrieved from three fields in "Custom axis orientation".  Then they are converted to degrees and displayed (lines 63-83 in the code linked above). But what I also want is to refresh them each time I click "Apply" button. Is there a way to achieve this?


What might help you accomplish this is to refactor the code into a class, so that you can start saving a bunch of the state. You create all the widgets, and the data structures to set things up, but your callbacks would either need to be wrapped up with closures to the references, or, would access them simply off the state of the instance:

So you can track your current degree values as they are updated, via:
self.curMoveDegVal = [] 

And I would suggest refactoring the block of code that updates your current text values, into a method that will do them using those instance attribute. Then its just a matter of tacking on a little more to your Accept button callback:

...
    applyButton = cmds.button(
        l='Apply',
        height=26,
        c=self.degToRad)
...
def updateText():
    # update the current degree text with
    # self.curMoveDegVal
    # self.curScaleVal

def clickCmd(self, *args):
    self.degToRad()
    self.updateText()

def degToRad(self):
    xDeg = self.xDeg
    yDeg = self.yDeg
    zDeg = self.zDeg
    relativeOpt = self.relativeOpt
    applyFor = self.applyFor
    ...


3. Combine formatting of "width", "align" and "precision" in Python.
Lines 75-83 are showing Python .format method. Each value has 3 float digit precision (.3f) and in addition to that they are right-aligned with 10 value (>10). Ok, that’s good but I what also want is to have three "0" before dot. So I want apply [width] to it (
http://docs.python.org/2.6/library/string.html#format-specification-mini-language). But I don’t know how to mix [align], [width] and [.precision] together in one formatting {}.


I'm not saying this is *the* way to do it, but this works, by breaking it up into a two step formatting:

vals = [.1, .23456, 12.23]
print 'Move: {0:>10} {1:>10} {2:>10}'.format(*map('{0:0>7.3f}'.format, vals))

First it converts the float to 3-precision, and left padded with zeros
Then it just does the right alignment to the strings.
 

4. Code improvements
Because it’s my first script/tool and because I have learning Python for only 3 month now – I don’t have too much experience in it – any advice about the code structure will be highly appreciated :*


That's some really clean looking python in my opinion!  
I wouldn't say "always write a class", but in your case you would probably want to explore writing one since you already have the need to store state, like references to your widgets, and your data structures, for use in callbacks.


PS. And as always sorry for my English.


Didn't even notice :-)
 

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/f002cc5e-5a10-4c24-b06b-a71c53860f90%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Justin Israel

unread,
Nov 26, 2013, 4:50:45 PM11/26/13
to python_in...@googlegroups.com

Correction on the first part... you are currently using a closure to pass the references of your widgets to your degree callback. It would end up being easier to switch to a class with instance attributes so that you can also reference your lists of values.

Peter Makal

unread,
Nov 28, 2013, 5:04:02 AM11/28/13
to python_in...@googlegroups.com
Thanks Justin for your answer! This trick with nested formatting {} utilizing map function is really neat! :D Well, about the second point though, unfortunately for me OOP is still some sort of black magic I just can't understand. So I write small script that should emulate my bigger tool just to test and learn things you said about this "refactoring the code into a class".

class MyWindow(object) :
   
def __init__(self) :
       
self.curMoveVal = cmds.manipMoveContext('Move', query=True, orientAxes=True)

   
def updateText(self, *args) :
       
return self.curMoveVal
       
   
def caoidWin(self) :
        testWindow
= 'someName'
       
       
if cmds.window(testWindow, ex=True) :
            cmds
.deleteUI(testWindow, window=True)
           
        testWindow
= cmds.window(
            testWindow
,
            title
='testWindow',
            wh
=(400,200))
       
        cmds
.columnLayout(
            adjustableColumn
=True,
            rowSpacing
=10)
       
        cmds
.text(
            l
='{0}'.format(self.updateText()))
           
        cmds
.button(
            l
='Apply',
            c
=self.updateText)
       
        cmds
.showWindow(testWindow)
       
a
= MyWindow()
a
.caoidWin()

Sorry if it doesn't make sense (as I said - I'm currently in a stone age with my programming skills). In this script my point was to just update what I have currently stored in Move Custom axis orientation after clicking "Apply"... and only that, no converting into degrees or other fancy things. But even with this simple task I failed and overall I don't know how approach this problem. First of all I don't know what to write into label=' ' in cmds.text to make it update each time.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

Justin Israel

unread,
Nov 28, 2013, 5:20:56 AM11/28/13
to python_in...@googlegroups.com
Yea I know the whole class stuff can be confusing at first. But it was either that, or suggest the thing that newcomers tend to do right away, which is start using a whole bunch of global variables, which is not a good approach. 
But your class looks really close to what might work. Not sure exactly what you wanted, but here is something that might be closer to a working example:

class MyWindow(object):

    def __init__(self):
        self.testWindow = 'someName'
        self.curMoveVal = cmds.manipMoveContext('Move', query=True, orientAxes=True)

    def updateText(self, *args) :
        cmds.text(self.text, e=True, l=str(self.curMoveVal))
        
    def caoidWin(self) :
        testWindow = self.testWindow
        if cmds.window(testWindow, ex=True) :
            cmds.deleteUI(testWindow, window=True)
            
        self.testWindow = cmds.window(
            testWindow,
            title='testWindow',
            wh=(400,200))
        
        cmds.columnLayout(
            adjustableColumn=True,
            rowSpacing=10)
        
        self.text = cmds.text(l=str(self.curMoveVal))
            
        cmds.button(
            l='Apply',
            c=self.updateText)
        
        cmds.showWindow(self.testWindow)

You would want to save a reference to the name of your text widget, so that you can refer to it later in a callback. I just changed your updateText() from returning a value, to actually updating your text widget with the latest value of self.curMoveVal. Thats kinda the idea, where you save all the references to the widgets that you will want to interact with later. Does that make more sense?



To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/4c4f7dd6-aefd-453c-a75d-7b1185f57baf%40googlegroups.com.

Peter Makal

unread,
Nov 28, 2013, 9:43:30 AM11/28/13
to python_in...@googlegroups.com

OMG! Thank you Justin, it works! Now I'm going to take some time to analyze what you write in your code example and try to make it work in my proper script. But once again - thank you!

To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages