Maya python connect to multiple input - python

I’m really sorry if I’m asking a question that’s been already answered but I couldn’t find an answer.
I’m writing a code that would allow me to connect a translate of several controllers into a blendWeighted node input channels. The amount of the controllers may vary depending on the selection. I’m struggling with the part where they need to connect to a single blendWeighted node input. Could someone tell me how I could connect every new controller to the next input channel of the blendWeighted node?
I’m sorry if my code is a bit childlike, I’m still learning ^^;
sel = mc.ls(sl=True, fl=True)
drvConnect = []
for i in sel:
name = i.split('_Crv')[0]
dGP = mc.createNode('transform', n='%s_Drv'%name, p=i)
drvConnect.append(dGP)
sh = mc.listRelatives(i, shapes=True)[0]
blendX = mc.createNode('blendWeighted', n='%s_X'%name)
blendY = mc.createNode('blendWeighted', n='%s_Y'%name)
blendZ = mc.createNode('blendWeighted', n='%s_Z'%name)
mc.connectAttr(dGP + '.translateX', blendX +'.input')
mc.connectAttr(dGP + '.translateY', blendY +'.input')
mc.connectAttr(dGP + '.translateZ', blendZ +'.input')

I assume you only want to create a single blendWeighted node. If that's the case, consider that the blendWeighted node's input attribute is an array attribute, so if you want to connect multiple items to it you would need to specify the target index.
For example, to connect the three translate outputs of a node to the first three inputs of the blend node you would use something like this:
mc.connectAttr('ctrl.translateX', 'blend.input[0]')
mc.connectAttr('ctrl.translateY', 'blend.input[1]')
mc.connectAttr('ctrl.translateZ', 'blend.input[2]')
(Maya will take care of creating the items in the array)
In our case you could simply keep a counter of the added items while you loop through the selection and the transform components (just a guide - not tested):
sel = mc.ls(sl=True, fl=True)
drvConnect = []
blendNode = mc.createNode('blendWeighted', n='blend_main')
for i in sel:
name = i.split('_Crv')[0]
dGP = mc.createNode('transform', n='%s_Drv'%name, p=i)
drvConnect.append(dGP)
for comp in ('X', 'Y', 'Z'):
mc.connectAttr(
'{}.translate{}'.format(dGP, comp),
'{}.input[{}]'.format(blendNode, blendIndex))
blendIndex += 1

Related

Catia select a feature from a specific instance in an assembly

Lets say I have an assembly like this:
MainProduct:
-Product1 (Instance of Part1)
-Product2 (Instance of Part2)
-Product3 (Instance of Part2)
-Product4 (Instance of Part3)
...
Now, I want to copy/paste a feature from Product3 into another one.
But I run into problems when selecting the feature programmatically, because there are 2 instances of the part of that feature.
I can't control which feature will be selected by CATIA.ActiveDocument.Selection.Add(myExtractReference)
Catia always selects the feature from Product2 instead of the feature from Product3. So the position of the pasted feature will be wrong!
Does anybody know this problem and has a solution to it?
Edit:
The feature reference which I want to copy already exists as a variable because it was newly created (an extract of selected geometry)
I could get help else where. Still want to share my solution. It's written in Python but in VBA its almost the same.
The clue is to access CATIA.Selection.Item(1).LeafProduct in order to know where the initial selection was made.
import win32com.client
import pycatia
CATIA = win32com.client.dynamic.DumbDispatch('CATIA.Application')
c_doc = CATIA.ActiveDocument
c_sel = c_doc.Selection
c_prod = c_doc.Product
# New part where the feature should be pasted
new_prod = c_prod.Products.AddNewComponent("Part", "")
new_part_doc = new_prod.ReferenceProduct.Parent
# from user selection
sel_obj = c_sel.Item(1).Value
sel_prod_by_user = c_sel.Item(1).LeafProduct # reference to the actual product where the selection was made
doc_from_sel = sel_prod_by_user.ReferenceProduct.Parent # part doc from selection
hb = doc_from_sel.Part.HybridBodies.Add() # new hybrid body for the extract. will be deleted later on
extract = doc_from_sel.Part.HybridShapeFactory.AddNewExtract(sel_obj)
hb.AppendHybridShape(extract)
doc_from_sel.Part.Update()
# Add the extract to the selection and copy it
c_sel.Clear()
c_sel.Add(extract)
sel_prod_by_catia = c_sel.Item(1).LeafProduct # reference to the product where Catia makes the selection
c_sel_copy() # will call Selection.Copy from VBA. Buggy in Python.
# Paste the extract into the new part in a new hybrid body
c_sel.Clear()
new_hb = new_part_doc.Part.HybridBodies.Item(1)
c_sel.Add(new_hb)
c_sel.PasteSpecial("CATPrtResultWithOutLink")
new_part_doc.Part.Update()
new_extract = new_hb.HybridShapes.Item(new_hb.HybridShapes.Count)
# Redo changes in the part, where the selection was made
c_sel.Clear()
c_sel.Add(hb)
c_sel.Delete()
# Create axis systems from Position object of sel_prd_by_user and sel_prd_by_catia
prod_list = [sel_prod_by_user, sel_prod_by_catia]
axs_list = []
for prod in prod_list:
pc_pos = pycatia.in_interfaces.position.Position(prod.Position) # conversion to pycata's Position object, necessary
# in order to use Position.GetComponents
ax_comp = pc_pos.get_components()
axs = new_part_doc.Part.AxisSystems.Add()
axs.PutOrigin(ax_comp[9:12])
axs.PutXAxis(ax_comp[0:3])
axs.PutYAxis(ax_comp[3:6])
axs.PutZAxis(ax_comp[6:9])
axs_list.append(axs)
new_part_doc.Part.Update()
# Translate the extract from axis system derived from sel_prd_by_catia to sel_prd_by_user
extract_ref = new_part_doc.Part.CreateReferenceFromObject(new_extract)
tgt_ax_ref = new_part_doc.Part.CreateReferenceFromObject(axs_list[0])
ref_ax_ref = new_part_doc.Part.CreateReferenceFromObject(axs_list[1])
new_extract_translated = new_part_doc.Part.HybridShapeFactory.AddNewAxisToAxis(extract_ref, ref_ax_ref, tgt_ax_ref)
new_hb.AppendHybridShape(new_extract_translated)
new_part_doc.Part.Update()
I would suggest a differed approach. Instead of adding references you get from somewhere (by name probably) add the actual instance of part to selection while iterating trough all the products. Or use instance Names to get the correct part.
Here is a simple VBA example of iterating one lvl tree and select copy paste scenario.
If you want to copy features, you have to dive deeper on the Instance objects.
Public Sub CatMain()
Dim ActiveDoc As ProductDocument
Dim ActiveSel As Selection
If TypeOf CATIA.ActiveDocument Is ProductDocument Then 'of all the checks that people are using I think this one is most elegant and reliable
Set ActiveDoc = CATIA.ActiveDocument
Set ActiveSel = ActiveDoc.Selection
Else
Exit Sub
End If
Dim Instance As Product
For Each Instance In ActiveDoc.Product.Products 'object oriented for ideal for us in this scenario
If Instance.Products.Count = 0 Then 'beware that products without parts have also 0 items and are therefore mistaken for parts
Call ActiveSel.Add(Instance)
End If
Next
Call ActiveSel.Copy
Call ActiveSel.Clear
Dim NewDoc As ProductDocument
Set NewDoc = CATIA.Documents.Add("CATProduct")
Set ActiveSel = NewDoc.Selection
Call ActiveSel.Add(NewDoc.Product)
Call ActiveSel.Paste
Call ActiveSel.Clear
End Sub

Creating a Hierachy in Maya

I've written some code for a tool that duplicates and moves controllers/objects to joints. Its really just code to copy and move one object to a list of other objects. Its very basic but it does work.
The pasted code is a snippet that I've taken out from the rest but will work if run in Maya.
This code in particular will create a nurbsCurve, put it inside a group, then move that group to the first object on the list. I then used a loop to do it again, but then parent the group underneath the nurbsCurve from the previous group to create a parent-hierarchy all the way down.
import maya.cmds as cmds
def setZero(target):
cmds.setAttr("%s.translateX" % target, 0)
cmds.setAttr("%s.translateY" % target, 0)
cmds.setAttr("%s.translateZ" % target, 0)
cmds.setAttr("%s.rotateX" % target, 0)
cmds.setAttr("%s.rotateY" % target, 0)
cmds.setAttr("%s.rotateZ" % target, 0)
selJoint = cmds.ls(selection = True, shortNames = True)
firstCtrl = cmds.circle(normal =( 1,0,0))
firstGrp = cmds.group(firstCtrl)
cmds.parent(firstGrp,selJoint[0])
setZero(firstGrp)
cmds.parent(firstGrp, world = True)
#Use loop for the rest of the joints
for joint in selJoint:
#Skip the first joint since its already done
if joint == selJoint[0]:
continue
circleCtrl = cmds.circle(normal =( 1,0,0))
offsetGrp = cmds.group(circleCtrl)
cmds.parent(offsetGrp, joint)
setZero(offsetGrp)
cmds.parent(offsetGrp, world = True)
cmds.parent(offsetGrp, firstCtrl) #Parent new offset Group to the old controller
firstCtrl = circleCtrl #The new controller is now the target for the next offset/ctrl to be parented under
It works as intended but I get this warning:
Warning: Cannot parent components or objects in the underworld
I've tried looking this issue up, but I haven't found anything on how to fix the problem. But I do know that the "underworld" is another space in Maya, and that it can cause issues down the line.
The warning is being thrown at the end on this line cmds.parent(offsetGrp, firstCtrl), and if you print out firstCtrl it'll output something like this:
[u'nurbsCircle1', u'makeNurbCircle1']
So what it's doing is it's trying to parent firstCtrl and 'nurbsCircle1' to 'makeNurbCircle1'. Since 'makeNurbCircle1' is a node with no transform, it throws that warning, because obviously you can't parent to something with no transform.
Luckily it's very easy to fix. Now that we know that it's a list, we just parent to the first index of it like so: cmds.parent(offsetGrp, firstCtrl[0])
If you want more info on this same warning then you can also check out this question.

Suggesting the next available IP network block

I was wondering if there's a good way to find the next available gap to create a network block given a list of existing ones?
For example, I have these networks in my list:
[
'10.0.0.0/24',
'10.0.0.0/20',
'10.10.0.0/20',
]
and then someone comes along and ask: "Do you have have enough space for 1 /22 for me?"
I'd like to be able to suggest something along the line:
"Here's a space: x.x.x.x/22" (x.x.x.x is something that comes before 10.0.0.0)
or
"Here's a space: x.x.x.x/22" (x.x.x.x is something in between 10.0.0.255 and 10.10.0.0)
or
"Here's a space: x.x.x.x/22" (x.x.x.x is something that comes after 10.10.15.255)
I'd really appreciate any suggestions.
The ipaddress library is good for this sort of use case. You can use the IPv4Network class to define subnet ranges, and the IPv4Address objects it can return can be converted into integers for comparison.
What I do below:
Establish your given list as a list of IPv4Networks
Determine the size of the block we're looking for
Iterate through the list, computing the amount of space between consecutive blocks, and checking if our wanted block fits.
You could also return an IPv4Network with the subnet built into it, instead of an IPv4Address, but I'll leave that as an exercise to the reader.
from ipaddress import IPv4Network, IPv4Address
networks = [
IPv4Network('10.0.0.0/24')
IPv4Network('10.0.0.0/20')
IPv4Network('10.0.10.0/20')
]
wanted = 22
wanted_size = 2 ** (32 - wanted) # number of addresses in a /22
space_found = None
for i in range(1, len(networks):
previous_network_end = int(networks[i-1].network_address + int(networks[i-1].hostmask))
next_network_start = int(networks[i].network_address)
free_space_size = next_network_start - previous_network_end
if free_space_size >= wanted_size:
return IPv4Address(networks[i-1] + 1) # first available address

Getting variables and calling functions with UI. Maya python

I am currently working on a little script that creates a crane-like rig automatically in Autodesk Maya, the user gets to choose the amount of joints by a UI.
My question is how do I take the integer input of the user and use it as the variable value for my "jointAmount"?
I am also wondering how I would be able to call my function(AutoCraneRig) to actually run the script from the UI. I have a "apply"-button but I am unsure how to connect it to my function.
I have seen similar posts like mine but I feel that the solutions shown are somewhat hard for me to understand and/or I can't really relate what is shown to my own problem.
If anything is unclear or more information is needed from me please don't hesitate to call me out.
Here is what my current UI look like
import maya.cmds as cmds
import pymel.core as pm
def jntctrl():
number = pm.intField(jnt, q=1, v=1)
print(number)
if pm.window("stuff", exists = True):
pm.deleteUI("stuff")
pm.window("stuff", t = "Crane Rig Generator", w=400, h=200)
pm.columnLayout(adj = True)
pm.text(label="Joint Amount:")
jnt = pm.intField(changeCommand = 'jntctrl()')
pm.button(label="Create Crane")
pm.showWindow()
#Defining how many joints the user want to have for their crane rig
jointAmmount = 5
#Defining how many controllers the user want to have to orient the crane.
#May not exceed the joint amount
controllerAmount = 5
def autoCraneRig():
#Creating the joints
for i in range(jointAmmount):
pm.joint()
pm.move(0, i, 0)
#Creating the controllers
for i in range(controllerAmount):
pm.circle()
pm.rotate (0,90,0)
pm.makeIdentity (apply= True)
#Creating the groups
for i in range(controllerAmount):
pm.group()
#Somehow one of the nurbs get parented to a group when running the script, here i select both the groups and then unparent them.
pm.select("group*", "nurbsCircle*")
pm.parent(world = True)
#Creating lists/dictionaries for the groups
#Since I wanted to parent my objects by their number I had to put all objects in lists/dictionries to get access.
groups = pm.ls('group*')
nbs = [int(n.split('group')[-1]) for n in groups]
groupDic = dict(zip(nbs, groups))
#Create a list/dictionary for the joints
joint = pm.ls('joint*', type='joint')
nbs = [int(n.split('joint')[-1]) for n in joint]
jointDic = dict(zip(nbs, joint))
common = list(set(groupDic.keys())&set(jointDic.keys()))
#Parenting the groups to the joints
for i in common:
pm.parent(groupDic[i], jointDic[i])
#Reseting the transformations of the groups and then unparenting them to still have the transformation data of the joints
pm.select("group*")
pm.makeIdentity()
pm.parent(world = True)
#Creating a list/dictionary for the nurbs aswell that will be parented to the groups in numeric order
nurbs_sh = pm.ls('nurbsCircle*', type='nurbsCurve')
#I had to get the transformation information from the nurbs before parenting them with anything would work(took a long time to get it right).
nurbs_tr = pm.listRelatives(nurbs_sh, p=1)
nbs = [int(n.split('nurbsCircle')[-1]) for n in nurbs_tr]
curveDic = dict(zip(nbs, nurbs_tr))
common = list(set(groupDic.keys())&set(curveDic.keys()))
#Parent the nurbs to the groups
for i in common:
pm.parent(curveDic[i], groupDic[i])
#Select the nurbs and reset transformations and then freeze transform
pm.select("nurbsCircle*")
pm.makeIdentity()
#Orient constrain the controllers/nurbs to the joints
for i in common:
pm.orientConstraint(curveDic[i], jointDic[i])
#Parent the 2nd group with the first controller. Do this for the whole hierarchy.
for i in common:
pm.parent(groupDic[i+1], curveDic[i])
#I'm getting keyError after I put the "+1" in my groupDic and I don't know why, although it still works, I guess.
autoCraneRig()
Here's an example for how to call a specific function/command when a button is clicked, and how to get the value of an int field. The key is in naming the fields, so you can reference the UI control later.
import pymel.core as pm
def ui():
if (pm.window("myWindow", exists=True)):
pm.deleteUI("myWindow")
window = pm.window("myWindow", t="My Window", w=400, h=200)
pm.columnLayout(adj=True)
pm.intField("myIntField")
pm.button("Button", aop=True, command="action()")
pm.showWindow(window)
def action():
print("Button clicked!")
value = pm.intField("myIntField", q=True, v=True)
print(value)
ui()
If you want to get more into making UI's, I would recommend you watch these two videos:
PySide UI Creation in Maya: Video One
PySide UI Creation in Maya: Video Two

Dynamically create objects and setting their attributes using suds lib in python

I'm using a raspberry to send some sensor data over to a SOAP webservice. RPi gets the data from the serial port. The format is 'DATE, VALUE1, VALUE2, VALUE3, VALUE4\r\n'. The webservice request looks like this
<sensors>
<Sensor>
<SensorId>int</SensorId>
<NodeId>int</NodeId>
<SensorTypeId>int</SensorTypeId>
<Value>double</Value>
<Status>string</Status>
<Date>dateTime</Date>
<Deleted>boolean</Deleted>
<Updated>boolean</Updated>
<RemoteId>int</RemoteId>
<DateOfLastUpdate>dateTime</DateOfLastUpdate>
<UserId>int</UserId>
<ErrorMessage>string</ErrorMessage>
</Sensor>
<Sensor>
<SensorId>int</SensorId>
<NodeId>int</NodeId>
<SensorTypeId>int</SensorTypeId>
<Value>double</Value>
<Status>string</Status>
<Date>dateTime</Date>
<Deleted>boolean</Deleted>
<Updated>boolean</Updated>
<RemoteId>int</RemoteId>
<DateOfLastUpdate>dateTime</DateOfLastUpdate>
<UserId>int</UserId>
<ErrorMessage>string</ErrorMessage>
</Sensor>
</sensors>
<username>string</username>
<password>string</password>
<UniqueClientID>string</UniqueClientID>
<project>string</project>
Each line I get from the serial port has 4 sensor values and the datetime these values were logged. So I need to create 4 objects with the SUDS library method client.factory.create() for each line I parse from the serial, add the values to each attribute and append() the objects to the Array of Sensor objects that the webservice accepts as its first parameter. The problem is I can't find a way to dynamically create the objects, enter the attributes' values, and append them to the big array. I'm going to parse probably 600lines from the serial port, so 4x600=2400 will need to be created. Hard-coding object names like this
while True:
serial_str = port.readline()
if serial_str:
string_list = serial_str.split(',')
date = 'T'.join( [ string_list[i] for i in [0, 1] ] )
temperature = string_list[2]
humidity = string_list[3]
rain = string_list[4]
wind = string_list[5].replace("\r\n","")
Sensor_obj1 = client.factory.create('Sensor')
Sensor_obj1.SensorId = -1
Sensor_obj1.NodeId = 1
Sensor_obj1.SensorTypeId = 2
Sensor_obj1.Value = temperature
Sensor_obj1.Status = ''
Sensor_obj1.Date = date
Sensor_obj1.Deleted = 0
Sensor_obj1.Updated = 0
Sensor_obj1.RemoteId = 0
Sensor_obj1.DateOfLastUpdate = datetime.now().strftime('%Y-%m-%dT%H:%M:%S')datetime.now()
Sensor_obj1.UserId = 0
Sensor_obj1.ErrorMessage = ''
Sensor_list_obj.Sensor.append(Sensor_obj1)
Sensor_obj2 = client.factory.create('Sensor')
...
would work if I only had 1 line to send, but even then its a bad programming style. I just got started with python 2.x, so I would appreciate if someone would point me to the right direction here. Thanks in advance
I am not very familiar with the problem at hand, but I would guess that you can add more parameters to the call to client.factory.create('Sensor') call. Does it accept, **kwargs, so that you can call:
client.factory.create('Sensor', SensorID=-1, NodeId=1, ...)
If not, you can always abstract the initialization by:
Creating a function that does all the asignments and hides away the 'gory' details. This is elegant enough, and probably you shouldn't try anything more advanced if you have just started programming in python.
Create a class Sensor, that inherits from the one of suds. Unfortunately, I don't know enough about the class of objects 'client.factory.create'.
Also, I think I would create a list of sensors (or dictionary, if that makes sense in your code), so that you append all the sensors to that structure, otherwise your program will be impossible to use if you have three or five sensors instead of 4.

Categories

Resources