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.
Related
I am extreme beginner in writing python scripts as I am learning it currently.
I am writing a code where I am going to extract the branches which I have which are named something like tobedeleted_branch1 , tobedeleted_branch2 etc with the help of python-gitlab module.
With so much of research and everything, I was able to extract the names of the branch with the script I have given bellow, but now what I want is, I need to delete the branches which are getting printed.
So I had a plan that, I will go ahead and store the print output in a variable and will delete them in a go, but I am still not able to store them in a variable.
Once I store the 'n' number of branches in that variable, I want to delete them.
I went through the documentation but I couldn't figure out how can I make use of it in python script.
Module: https://python-gitlab.readthedocs.io/en/stable/index.html
Delete branch with help of module REF: https://python-gitlab.readthedocs.io/en/stable/gl_objects/branches.html#branches
Any help regarding this is highly appreciated.
import gitlab, os
TOKEN = "MYTOKEN"
GITLAB_HOST = 'MYINSTANCE'
gl = gitlab.Gitlab(GITLAB_HOST, private_token=TOKEN)
# set gitlab group id
group_id = 6
group = gl.groups.get(group_id, lazy=True)
#get all projects
projects = group.projects.list(include_subgroups=True, all=True)
#get all project ids
project_ids = []
for project in projects:
project_ids.append((project.id))
print(project_ids)
for project in project_ids:
project = gl.projects.get(project)
branches = project.branches.list()
for branch in branches:
if "tobedeleted" in branch.attributes['name']:
print(branch.attributes['name'])
Also, I am very sure this is not the clean way to write the script. Can you please drop your suggestions on how to make it better ?
Thanks
Branch objects have a delete method.
for branch in project.branches.list(as_list=False):
if 'tobedeleted' in branch.name:
branch.delete()
You can also delete a branch by name if you know its exact name already:
project.branches.delete('exact-branch-name')
As a side note:
The other thing you'll notice I've done is add the as_list=False argument to .list(). This will make sure that you paginate through all branches. Otherwise, you'll only get the first page (default 20 per page) of branches. The same is true for most list methods.
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.
In order to get well acquainted with Python for Nuke I am creating a small game that takes place in the Node Graph, but I hit a snag while trying to move my "character" around using a function. The character is a Dot and the function is trying to read its position in X and Y to determine in which direction it can move, then provide the player with those options and finally move the character in the direction chosen. The function must receive the character as an input but that's where I am having trouble, this is the simplified version of that part of the code:
global currentPosX
global currentPosY
currentPosX = 0
currentPosY = 0
def moveArea(self, node):
charT = node
print = currentPosX
currentPosX = charT['xpos'].value()
currentPosY = charT['ypos'].value()
char = nuke.nodes.Dot(hide_input=1, xpos=490, ypos=70)
moveArea(char)
I have tried many things and this code you see here is where I just couldn't think of any other options, I believe the problem is in how I input the 'char' node into the function but I could not find any resource that made it clear. Any help would be appreciated!
I've created a simplified function with some useful nuke commands thrown in that might be useful to you. For example, a function outside of a class doesn't need a self argument. This code only creates a characterDot if one doesn't already exist, so you'll be able to execute it multiple times and see the dot move further.
def moveArea(node, moveX=0, moveY=0):
# query current position of input node
currentPosX = node['xpos'].value()
currentPosY = node['ypos'].value()
# calculate new position based on arguments
newPosX = currentPosX + moveX
newPosY = currentPosY + moveY
# actually move the node
print "moving %s from (%s,%s) to (%s,%s)" % (node.name(), currentPosX, currentPosY, newPosX, newPosY)
node['xpos'].setValue(newPosX)
node['ypos'].setValue(newPosY)
# create the node for the very first time with a unique name (if it doesn't exist)
if not nuke.exists('characterDot'):
characterDot = nuke.nodes.Dot(hide_input=1, xpos=490, ypos=70, name='characterDot')
# find the node based on the name whenever you want
char = nuke.toNode('characterDot')
# run the move function on the character
moveArea(char, 100, 20)
Some syntax errors aside, your original code wasn't hugely problematic - though you never actually set new values to the node (using setValue) and only queried the nodes positions. Passing in the entire object is acceptable practice as far as I'm concerned! Though as part of working with nuke will likely involve lots of selecting, creating and deselecting nodes - so I've added a bit of code that will find the dot again based on its unique name.
My advice would be to create a class for the character dot, which has move functions that find it then move it.
Let me know if that helps, or if you can provide a slightly more complex demonstration of the issue you're having!
I want to have some simple interfaces for the users to connect some pre-defined modules (I'm free to modify them if I have to).
Each module has a few named inputs and outputs.
One module may connect to a few modules.
A few modules can connect to a single module.
This is what I have in my mind so far:
Option 1:
Each module has "in" and "out" dictionary for input and output, and the connection is made through code like this:
a.out["out 1"].to(b.in["in 1"]) # connect a's "out 1" output to b's "in 1" input
a.out["out 2"].to(b.in["in 2"]) # connect a's "out 2" output to b's "in 2" input
But this looks quite tedious, so I came up with Option 2:
# connect a to b and c, then list which inputs connect to which outputs
a.to(b,{"out 1":"in 1",
"out 2":"in 2"},
c,{"out 4":"in 1",
"out 3":"in 2"})
This seems to look better as it's clearer to me which modules are connected and also the mapping between their outputs and inputs are clearly listed.
I wonder if there is any room to improve the above, to clearly show:
module level connections, e.g. module a connect to module b
outputs and inputs connections
simple and clear interface. By 'simple', I mean less typing; 'clear' means easy to understand. I understand that sometimes I can't have both in that case, 'clear' interface is preferable.
I'm not proficient in Python(2.7) so there might be some syntax or operator or data structure that I am not aware of but I may be able to take advantage for this purpose.
I have run into this exact issue, the issue of being able to clearly describe directed graphs linearly. None are particularly pleasant... Personally, I believe showing how the sources and sinks (node inputs and outputs) connect is more important than showing which nodes are connected, because the relationship is more specific.
Of the two designs you listed I would recommend the first, since it is clearer. It shows exactly how each input and output connects. However, I would slightly augment your implementation. I would make the node class capable of creating new handles for inputs and outputs that are actual attributes in the object. Then use those handles when describing the graph and capturing traffic, etc. Making the inputs and outputs actual objects instead of labels means construction-time checking and the ability to generate better error messages. For example:
b = Node()
b.input("in1", "in2") # this creates a new input object that is located in 'b' with the attribute name of the given string
b.output("out1") # this could be in the constructor
c = Node(in=["in1", "in2"], out=["out1"]) # like so
# describe the connections
a.out1.to(b.in1)
a.out2.to(b.in2)
a.out3.to(c.in2)
a.out4.to(c.in1)
# maybe syntax like 'a.out3 >> c.in2' would be prettier?
module_graph_output = c.out1.capture()
# do stuff with the graph output
Additionally, the module definitions can be passed these output and input object handlers for the user as they implement the module logic.
All this aside, how you are going to run the module graph is the bigger issue. If I'm not intruding, what is the application of this?
I'll try to simplify my problem. I'm writing a test program using py.test and appium. Now:
In the application I have 4 media formats: Video, Audio, Image and Document.
I have a control interface with previous, next, play , stop buttons.
Each media formats has a unique ID like
video_playbutton, audio_playbutton, document_playbutton, image_playbutton, video_stopbutton audio_stopbutton ...etc etc.
But the operation I have to do is the same for all of them e.g press on playbutton.
I can address playbutton of each when i give them explicitly like this
find_element_by_id("video_playbutton")
And when i want to press on other playbuttons I've to repeat above line each time. Like this:
find_element_by_id("video_playbutton")
find_element_by_id("audio_playbutton")
find_element_by_id("image_playbutton")
find_element_by_id("document_playbutton")
And because I'm calling this function from another script I would have to distinguish first what string I got e.g:
def play(mediatype):
if mediatype == "video"
el = find_element_by_id("video_playbutton")
el.click()
if mediatype == "audio"
el = find_element_by_id("audio_playbutton")
el.click()
if .....
What is the best way to solve this situation? I want to avoid hundreds of if-statements because there is also stop, next , previous etc buttons.
I'm rather searching for something like this
def play(mediatype)
find_element_by_id(mediatype.playbutton)
You can separate out the selectors and operations in two dictionaries which scales better. Otherwise the mapping eventually gets huge. Here is the example.
dictMedia = {'video':['video_playbutton', 'video_stopbutton','video_nextbutton'], 'audio':['audio_playbutton', 'audio_stopbutton', 'audio_nextbutton']}
dictOperations = {'play':0, 'stop':1, 'next':2}
def get_selector(mediatype, operation):
return dictMedia[mediatype][dictOperations[operation]]
print get_selector('video', 'play')
PS: The above operation doesn't check for key not found errors.
However, I still feel, if the media specific operations grow, then a page object model would be better.