As I continue to study For Loops: I've run into some annoying errors. The problem is the script does exactly what I want it to. It deletes the null groups under the demo joints: but unlike other loops I've made for renaming which can be closed with a transform flag in the cmds.ls command: cmds.listRelatives doesn't allow a transform flag to close out the loop. You run the script by simply clicking Build Examples then hitting Delete Waste Groups
I've tried every flag according to the Maya documentation: but nothing seems to be closing the loop. I dont know if I need another variable, or a combination of some flags: or if I am using the wrong type of wording: but ideally what I would like this script to do is simply close out the loop so I dont get the error Error: No object matches name: curve
'''
import DS_wasteGroup_cleanerDemo
reload (DS_wasteGroup_cleanerDemo)
DS_wasteGroup_cleanerDemo.gui()
'''
import re
import maya.cmds as cmds
import maya.mel as mel
if cmds.window("renameWin", exists =True):
cmds.deleteUI("renameWin", window = True)
myWindow = cmds.window("renameWin",t='DS_wasteGroup_cleanerDemo',w=200, h=500, toolbox=True)
column = cmds.columnLayout(adj=True)
def gui():
cmds.button( label="Build Examples", c = buildExamples)
cmds.separator( w=200, h=3)
cmds.button( label="Delete Waste Groups", c = deleteWasteGrp)
cmds.separator( w=200, h=9)
cmds.setParent('..')
cmds.showWindow(myWindow)
def buildExamples(*args):
cmds.group(n='exampleGroup1',world=True,empty=True)
cmds.joint(n='demoJoint1')
cmds.group(n='curve1',world=True,empty=True)
cmds.parent('curve1','demoJoint1')
cmds.joint(n='demoJoint2')
cmds.parent('demoJoint2','exampleGroup1')
cmds.group(n='curve2',world=True,empty=True)
cmds.parent('curve2','demoJoint2')
cmds.joint(n='demoJoint3')
cmds.parent('demoJoint3','exampleGroup1')
cmds.group(n='curve3',world=True,empty=True)
cmds.parent('curve3','demoJoint3')
cmds.joint(n='demoJoint4')
cmds.parent('demoJoint4','exampleGroup1')
cmds.group(n='curve4',world=True,empty=True)
cmds.parent('curve4','demoJoint4')
cmds.joint(n='demoJoint5')
cmds.parent('demoJoint5','exampleGroup1')
cmds.group(n='curve5',world=True,empty=True)
cmds.parent('curve5','demoJoint5')
def deleteWasteGrp(*args):
grpList = cmds.listRelatives('demoJoint*',p=True,f=True)
for name in grpList:
print(grpList)
cmds.delete('curve*')
My apologies if I'm posting simple questions. I do write Python scripts to automate the most tedious tasks in rigging: but my knowledge is only intermediate. I want to learn more python so my scripts arent so clunky and brute forced: as well as the fact that I need them to be more adaptable to various types of characters: so any resources that dumb all this down would also be appreciated. Thank you for your help.
The error is correct, because the very first time the for loop executes, all "curve" obects are deleted, then in the next iteration, the same command does not find any curve objects because they are already deleted. If you place the delete command outside the for loop, the error should disappear.
Honestly I would take a whole different approach as you're hard-coding everything which could easily lead to disaster. When I mean hard-code, I mean you're trying to parent, let's say, "demoJoint2" to an object. This is bad because why are you assuming that "demoJoint2" even exists? If you create an object with a specific name that already exists, Maya will auto-rename the new object, and now you're referencing the wrong one right off the bat! Instead when you create your objects, capture their names in a variable then work with that, otherwise you'll be constantly shooting yourself in the foot.
Here's the same script with an approach I would try instead:
import maya.cmds as cmds
def gui():
if cmds.window("renameWin", exists=True):
cmds.deleteUI("renameWin", window=True)
myWindow = cmds.window("renameWin", t="DS_wasteGroup_cleanerDemo", w=200, h=500, toolbox=True)
column = cmds.columnLayout(adj=True)
cmds.button(label="Build Examples", c=buildExamples)
cmds.separator(w=200, h=3)
cmds.button(label="Delete Waste Groups", c=deleteWasteGrp)
cmds.separator(w=200, h=9)
cmds.setParent("..")
cmds.showWindow(myWindow)
def buildExamples(*args):
root = cmds.group(n="exampleGroup1", world=True, empty=True)
for i in range(5): # Loop to the amount of joints you want to create.
jnt = cmds.createNode("joint", name="demoJoint" + str(i + 1)) # Use `i` to help name the object.
jnt = cmds.parent(jnt, root)[0] # Parenting changes its long name, so recapture the joint in a variable.
crv = cmds.group(n="curve" + str(i + 1), world=True, empty=True) # Create empty group.
cmds.parent(crv, jnt) # Parent curve to joint.
def deleteWasteGrp(*args):
jnts = cmds.ls("demoJoint*", long=True, type="joint") # Get all `demoJoints`.
children = cmds.listRelatives(jnts, f=True, children=True, type="transform") or [] # Get all of their children, and only get transform types.
curves = [obj for obj in children if obj.split("|")[-1].startswith("curve")] # Don't assume we got the right objects. Run a final loop to collect any object that starts with `curve`. Need to use split as we're looping through long names but need to check its short name.
if curves: # `cmds.delete` will error if this list is empty, so don't assume.
cmds.delete(curves) # Delete all curves at once.
gui()
Now I can hit the build button as much as I want with no issues, and delete all the curves when pressing the delete button.
A few more notes:
Notice in buildExamples I'm using a loop to create all the objects instead of reusing redundant code that does the same thing. You could even have a spinbox in your gui that defines how many joints it creates now, where as before it wasn't possible because the count was hard-coded.
cmds.listRelatives does have a way to filter objects by transforms by setting parameter type="transform". In fact you'll see many commands have this same parameter (again start checking docs).
cmds.listRelatives('demoJoint*',p=True,f=True) was grabbing the joint's parent, not its children. The docs clearly state this.
Running cmds.delete('curve*') is going to delete ALL objects with names that start with curve, and since you're running this in a loop it's trying to do this multiple times.
maya.cmds is not pymel. There's a whole separate module called pymel.
If you're unsure with any parts of the code try adding in a print statement to see what it's doing.
I feel like you're going about this whole process a bit wrong, and I would love to elaborate if you're interested, but for now here is a fix for your loop situation:
def deleteWasteGrp(*args):
curveList = cmds.ls('curve*',transforms=True)
try:
cmds.delete(curveList)
print('Deleted the following objects: {}'.format(curveList))
except Exception as e:
cmds.warning('Cleanup failed: {}'.format(e))
The cmds.delete method accepts a list parameter, which in your case is the easiest way to get the job done. Keep in mind that when you delete a parent object, you also delete its children, so depending on your circumstances deleting objects can be order-specific.
Throwing any "likely to fail" calls in a try/except clause is generally a good idea, as it lets you handle the error gracefully. Be careful, however, to not suppress it and just move on -- you at the very least need to alert the user adequately.
Lastly, your buildExamples method will most likely fail if you run it more than once. Because you are addressing objects by string literals (hard coded names) instead of keeping track of their actual names (and full path). You will likely see this error eventually:
# Error: ValueError: file <maya console> line ??: More than one object matches name: demoJoint1 #
Edit: Some elaborations as requested
The commands cmds.group and cmds.joint return a string value indicating the actual name of the object created (in create mode). It's usually a good idea of storing this value in case Maya decides to name your object slightly differently than what you are expecting, usually when there is a naming clash. Eg:
print cmds.group(name='test', world=True, empty=True)
# Returns: test
print cmds.group(name='test', world=True, empty=True)
# Returns: test1
Example of how to capture object names as you create them. I've concatenated your five identical(ish) calls to create joints and curves in this loop:
import maya.cmds as cmds
topGroupName = 'exampleGroup'
actualTopGroupName = None
# Create top level group
actualTopGroupName = cmds.group(n=topGroupName, world=True, empty=True)
# Loop through 5 times and do the following:
for i in range(5):
# PS: hash character in name indicates "next available number"
cmds.select(clear=True)
jnt = cmds.joint(n='demoJoint#')
crv = cmds.group(n='curve#',world=True,empty=True)
cmds.parent(crv, jnt)
cmds.parent(jnt, actualTopGroupName)
Example of how to narrow down which objects to search for with cmds.ls:
topGroupName = 'exampleGroup'
print cmds.ls('|{}*|*|curve*'.format(topGroupName))
# Returns: [u'curve1', u'curve2', u'curve3', u'curve4', u'curve5']
# The string .format() expression is just a dynamic way of writing this:
# |exampleGroup*|*|curve*
Vertical pipes (|) indicate levels in a hierarchy, similar to how slashes (/) work in URLs. And asterisks/wildcards (*) indicate "any character, or none".
Hope this helps you along your way a little bit.
I am creating a nuke node to easily manipulate my different lights in a node. I want to code it so that it doesn't matter how many lights are in any given file, and the code can figure it out and display the necessary tools to modify them. To achieve this, I first identify how many lights are in the file and store that value, which then gets fed to a 'for' cycle to create as many nodes and tools as lights are on the original file. 'tempNumber' is the number assigned to the original Lights on the file, and it helps create and manage all the tools, labeled to match the light they are modifying. It becomes complicated because the names of the nodes generated look like this:
globals()['expoParent{}'.format(tempNumber)]
Up to now everything has been working fine but I am stuck trying to link two different Exposure nodes to reflect the same values as the user modifies them. I created this code for that purpose on a separate script:
par = nuke.nodes.EXPTool(mode = 'Stops', name = 'Parent')
chi = nuke.nodes.EXPTool(mode = 'Stops', name = 'Child')
par.knob('knobChanged').setValue('chi["red"].setValue(par["red"].value())\nchi["green"].setValue(par["green"].value())\nchi["blue"].setValue(par["blue"].value())')
This previous bit of code works perfectly, it creates 2 Exposure nodes and it links the Child to the Parent so that any change made to the Parent is then automatically passed along to the Child immediately. Now the problem comes when I try to replace the variables par and chi with globals()['expoParent{}'.format(tempNumber)] and globals()['expoChild{}'.format(tempNumber)] . At this point the code stops working and the link is not successful. I have read plenty of people saying that variables should not be created through a for loop but I don't see any other way of doing it.
Why do it with a knobChanged function at all when you can just link the node fields with expressions?
Here's an example that links two grade nodes (Grade1 and Grade2) with Grade1 as the parent. You'll need to have these in your script before running the code. Though you could also create them procedurally.
parent = nuke.toNode('Grade1')
child = nuke.toNode('Grade2')
child['multiply'].setExpression('%s.multiply' % parent.name())
This is a lot cleaner, and would be represented visually in the node tree too.
I want to make it so that my program will stop running and print object is out of bounds if an object goes say into the negative z part of the plane in blender.
the objects name is Cube.031. I will sudo code what I want to do I just am not sure about sure how to do the syntax for it.
if(Cube.031.zLocation < 0)
print(object is out of bounds)
end
If you know some programming, learning python won't take long.
For the blender specific info, almost everything is accessed through the bpy module, the API reference is online.
You can refer to an object by name in bpy.data.objects[]. There are also other lists available, like bpy.context.selected_objects[] and bpy.context.visible_objects[].
An objects location is an array of three values (x,y,z), you can either access the z location as location.z or location[2].
import bpy
obj = bpy.data.objects['Cube.031']
if obj.location.z < 0:
print('object is out of bounds')
If you wanted to go through all selected objects
for obj in bpy.context.selected_objects:
if obj.location.z < 0:
print('object {} is out of bounds'.format(obj.name))
Note that v2.80 is due for release soon and has some changes to the API, if you are just starting with blender you may want to start with 2.80. You will also find blender.stackexchange a better place to ask for blender specific help.
I have the next setup: I have a sphere which has a morpher modifier. This morpher modifier has a certain amount of channels filled with morph targets aka sub animations. Now I want to add a controller to each of these subanimations, more specifically a controller with a float script. I have the code snippet that should work but when I go to the curve editor, the morph channels/ sub animations did not change controller, nor is the value of their controller changed.
import MaxPlus
target = MaxPlus.INode.GetINodeByName('Sphere001')
#Retrieve the morpher modifier
mod = target.GetModifier(0)
#ID of a float script controller
id = MaxPlus.Class_ID(1233584870,1911625032)
#Create float controller
float_co = MaxPlus.Factory.CreateFloatController(id)
#Retrieve the first morph channel / sub animation
sub = mod.GetSubAnim(1)
#Controller is assigned to the sub animation
sub.AssignController(float_co,1)
#Basic test which assigns 20 to the sub animation
float_co.ParameterBlock.Script.Value = '20'
When I add a wrong value to the script, for example:
float_co.ParameterBlock.Script.Value = '=20'
I receive an error and the usual window when you manually add a controller to an object or node. However the strange thing is that at the top of the window: the name of the object to which it is connected, does not show. See figure for clarification:
Can someone tell me what I'm doing wrong? Thank you!
I solved it by using the ugly way:
import MaxPlus
test = MaxPlus.FPValue()
success = MaxPlus.Core.EvalMAXScript(string_with_command,test)
This is used twice: first to create the float script controller and a second time to add the script to the controller. Be careful if anyone wants to try this, the script for the controller needs to be a string. Do not use
x as string
with the expressing you want to use as script for the float script controller since maxscript will evaluate x on the timeframe you are currently in 3ds max and will convert this value to a string. This value will be used as script for all time frames which clearly is not what you want. A small hack I used was:
script_value_example = '"amax #(0, ($sphere.position.x - cube.position.y))"'
This is still a string for python and maxscript will see the " " and will interpret it as a string. The other way around doesn't work, maxscript does not interpret ' ' as string.
Maybe this will help someone in the future. Also if someone knows the proper way to do it using the code in the question, please leave a reply, I'm interested to know.
I have an extremely long program for a Canasta game that I am writing, in it there is a variable that ends the turn of the player if it equals a certain value. However, the turn repeats itself.
I have read many different articles and posts on forums like this one, but none of these seem to work. I even have a debug system to print the value of the variable before the if statement, and the variable is the right value, but the if statement doesn't see the same value.
I can't give the entire code, as it is extremely long, but I can give the sections that use this variable. I am using Python 2.7.2 and cannot make files separate from the main file to import.
The first line of the program is:
endTurn=1
The following function is called when conditions to win the game are met, but I have not been able to test that this works because of the bug.
def winGame(player):
global endTurn
gameWinner=player
gameWinner["score"]+=100
endTurn=0
The following function is called when a move is made. The variable decide is a raw_input() variable.
def move(player,decide):
global endTurn
theMove=decide.lower()
#if player says to end turn
if theMove=="end":
#until player discards something
discarded=0
while not discarded:
displayHand(player)
#ask player for discard
discard=int(raw_input(" Enter the list number of the card you wish to discard:"))-1
#if discard ID is in player's hand length
if not discard<0 and not discard>len(player["hand"])-1:
#add card to discard pile and remove from hand
discardPile.append(player["hand"][discard])
del(player["hand"][discard])
discarded=1
debug("Before changing, endTurn is %s"%str(endTurn))
endTurn = 0
debug("After changing, endTurn is %s"%str(endTurn))
if theMove=="new book":
newBook(player)
if theMove=="add to book":
addBook(player)
Here is where the turn should be ended. turn(thePlayer) repeats the function. The debug statement shows the correct value, 0, but the if still reads a 1. The function this is in also has the global endTurn at the top.
debug("If ending turn, endTurn of %s should be 0."%str(endTurn))
if endTurn==1:
turn(thePlayer)
Any help is greatly appreciated!
EDIT: The code is available at http://labs.codecademy.com/CV9z#:workspace. I ask that anyone viewing the code does not modify it, so that other people can see the true code.
I fixed the bug. I removed the endTurn variable and instead made the function that ends the turn just do the turn of the next player. It also simplified my code a bit.
I am wondering though, is using this method of running a function inside of itself over and over and over again (without leaving) rather messy or maybe slowing down my program?
EDIT: I now realize that this answer does not exactly help with anyone else who is stuck with global variables...
The previous answer I posted was just wrong, as it was based on a misinterpretation of the namespace docs as I had read them (Thanks to TheifMaster for pointing out my error), so this is a SEVERELY EDITED ANSWER:
The problem is in the while loop that I was able to read when the link to the entire code was posted after the erroneous answer I gave earlier.
while gameWinner==0:
endTurn=1
turn(player1)
if not gameWinner==0:
endTurn=1
turn(player2)
The game can never get to player two in this loop until gameWinner!=0.
I added the entire code to my sandbox and changed it to:
while gameWinner==0:
endTurn=1
turn(player1)
if gameWinner==0: #if player1 did not win yet
endTurn=1
turn(player2)
However, the game is still buggy... No one can win! gameWinner is not changed globally by the winGame() function until you add it to the global statement as I have shown here.
def winGame(player):
global endTurn, gameWinner
gameWinner=player
gameWinner["score"]+=100
debug("gameWinner == " + str(gameWinner))
endTurn=0