Blender changes the names of my objects - python

I'm using the python API for a "scatterplot" in blender.
The data is a dictionary which maps names to lists of 3D points, so there are named clouds of points.
I need to look at each cloud individually and hide the others, this is my setup:
for each name in the dict, I create an empty object
for each 3D point belonging to this name, I create a small cube, position it and reparent it to the empty object.
Now I can hide the parent objects in the 3D view.
The program works fine, but there is one strange problem: The names are important, I need to be able to find them in the sceneview. But blender changes the names. A name like "TopDown" becomes "TopDown.001". This happens despite the fact that there are no other objects with this name.
Here is my code:
for plotname, positions in points.items():
bpy.ops.object.add(type='EMPTY')
bpy.context.active_object.name = plotname
bpy.context.active_object.location=(0,0,0)
print(plotname) #<---------------here the name is still correct
for position in positions:
me = bpy.data.meshes.new(plotname + 'Mesh')
ob = bpy.data.objects.new(plotname+"Mesh", me)
ob.location = (position[0], position[1], position[2])
ob.show_name = True
bpy.context.scene.objects.link(ob)
me.from_pydata(verts_loc, [], faces)
me.update(calc_edges=True)
ob.parent=bpy.context.active_object
The actual program is a little longer, verts_loc and faces have been set up before this snippet. They represent a cube.
How can I ensure that plotname stays plotname and doesn't become plotname.001 ?
UPDATE
Clarification: It doesn't matter that points within the plot are renamed. Something like "plotnameMesh.001" and "plotnameMesh.002" is no problem. But the parent objects are renamed to.
In fact the sole purpose of appending "Mesh" to the object names is to keep the toplevel plotname unique.

The for position in positions: implies you are creating multiple objects at different locations for each plotname. One will keep the plotname while the others will get numeric extensions.
for position in positions:
me = bpy.data.meshes.new(plotname + 'Mesh')
ob = bpy.data.objects.new(plotname+"Mesh", me)
Is each position unique or are you adding multiple objects at each position?
You will also want to ensure you delete the previous creations before re-running your script.
When you have many objects with similar names you can use Select->Select Pattern to select them. In python you can do that with
[setattr(obj, 'select', True) for obj in bpy.data.objects if obj.name.startswith(plotname)]

Related

Python - Objects of a list matching objects positions of another list

I'm trying to match positions of objects with similar names from different lists in my maya scene using python.
i.e In my scene there are 100's of big boxes ("Box_01_obj","Box_02_obj"...) and 100's of small boxes ("Box_01_obj_small", "Box_02_obj_small"...) and I'm trying to make those small boxes match big boxes location.
How could I do this using python?
import maya.cmds as mc
for SmallBox in mc.ls("*_small"):
BigBox = SmallBox.replace("_small","")
if mc.objExists(BigBox):
BigBoxPositionX = mc.getAttr(BigBox+".tx")
mc.setAttr(SmallBox+".tx", BigBoxPositionX)
BigBoxPositionY = mc.getAttr(BigBox+".ty")
mc.setAttr(SmallBox+".ty", BigBoxPositionY)
BigBoxPositionZ = mc.getAttr(BigBox+".tz")
mc.setAttr(SmallBox+".tz", BigBoxPositionZ)
This will work, assuming that the boxes share the same parent, or are unparented. If not, you'll have to use a more complex align function or parent constrain the small boxes to the big ones and then remove the parent constraint.

Abaqus turn surfaces into sets

I've been trying to find the centers of two surfaces in my model (see photo), but don't manage to do so. They are element surfaces (faces) and there is no option in the query to find centers of element surfaces, only of element sets. Finding the center of node sets is also fine, but my node sets do not appear in the tools -> query -> mass property options.
And I can't find an option to convert my element surfaces into element sets.
What I eventually want is to find the centers of both red highlighted surfaces and draw a line between them. Can anyone help me with this?
If your surfaces contain elements then you can always retrieve them and create a set on the base of it.
m = mdb.models['myModel']
a = m.rootAssembly
es_l5_inferior_elems = a.surfaces['ES_L5INFERIOR'].elements
es_l5_inferior_elems_set = a.Set(
name='ES_L5INFERIOR_ELEMS_SET',
elements=es_l5_inferior_elems
)
If you want to get a nodes set, I am afraid you have to loop through all the elements in es_l5_inferior_elems and use the getNodes() method of a MeshElement object (keeping in mind that there will be repetitions to delete).
To get the mass/volume center you can use the dedicated method getMassProperties() of the Part or rootAssembly. By the way, to use it you do not need Sets (simply give the elements/nodes array)!
mass_prop = a.getMassProperties(regions=es_l5_inferior_elems)
print(mass_prop['volumeCentroid'])
print(mass_prop['centerOfMass'])

Instancing maya objects with a sequential suffix, object name string not seen by cmds.instance

I have a question about string usage in lists in python for Maya. I am writing a script meant to take a selected object, then instance it 100 times with random translate, scale, and orient attributes. The script itself works and does what it's meant to, however I'm not being able to decipher how to instance the objects with the original object name, and then add a suffix that ends with "_instance#", where # assigns 1, 2, 3, etc. in order to the copies of the original mesh. This is where I'm at so far:
#Capture selected objects, sort into list
thing = MC.ls(sl=True)
print thing
#Create instances of objects
instanceObj = MC.instance(thing, name='thing' + '_instance#')
This returns a result that looks like "thing_instance1, thing_instance2".
Following this, I figured the single quote around the string for the object was causing it to just name it "thing", so I attempted to write it as follows
MC.instance(thing, name=thing + '_instance1'
I guess because instance uses a list, it's not accepting the second usage of the string as valid and returns a concatenate error. I've tried rewriting this a few times and the closest I get is with
instanceObj = MC.instance(thing)
which results in a list of (pCube1,2,3,4), but is lacking the suffix.
I'm not sure where to go from here to end up with a result where the instanced objects are named with the convention "pCube1_instance1, pCube1_instance2" etc.
Any assistance would be appreciated.
It is not clear if you want to use only one source object or more. In any case the
MC.ls(sl=True)
returns a list of strings. And concatenating a list and a string does not work. So use thing[0] or simply
MC.ls(sl=True)[0]
If you get errormessages, please always include the message in your question, it helps a lot to see what error appears.

How do I numerically position a barcode in reportlab?

I have a bit of a specific case that I can't seem to figure out a solution to. I'm writing a shipping label template object in ReportLab for Python. I have the following code that creates a barcode as a drawing.
uspsBarcode = createBarcodeDrawing('USPS_4State', value=self.imbval, routing=self.zip4.replace('-',''))
print uspsBarcode.getBounds() # print out of position and scale
Using that code, I later add it to a shape group, and that shape group gets returned. So I need the barcode to be positioned relative to the object. I can't seem to find any way to pass positioning to this, even though I've dug through the inheritance. Though as you can see from the print, positioning is set somwhere.
Just for anyone else that runs across this issue. It turns out that if you put the barcode drawing in a shape group, the shape group container can be moved around numerically with the shift function.
uspsBarcode = shapes.Group()
bc = createBarcodeDrawing('USPS_4State', value=self.imbVal, routing=self.zip4.replace('-',''))
uspsBarcode.add(bc)
uspsBarcode.shift(self.x+(s*0.2), self.y)

How to access a specific class instance by attribute in python?

Say I have a class Box with two attributes, self.contents and self.number. I have instances of box in a list called Boxes. Is there anyway to access/modify a specific instance by its attribute rather than iterating through Boxes? For example, if I want a box with box.number = 40 (and the list is not sorted) what would be the best way to modify its contents.
If you need to do it more frequently and you have unique numbers, then create a dictionary:
numberedBox = dict((b.number, b) for b in Boxes)
you can then access your boxes directly with numbers:
numberedBox[40]
but if you want to change their number, you will have to modify the numberedBox dictionary too...
Otherwise yes, you have to iterate over the list.
The most straightforward way is to use a list comprehension:
answer=[box for box in boxes if box.number==40]
Be warned though. This actually does iterate over the whole list. Since the list is not sorted, there is no faster method than to iterate over it (and thus do a linear search), unless you want to copy all the data into some other data structure (e.g. dict, set or sort the list).
Use the filter builtin:
wanted_boxes = filter(lambda box: box.number == 40, boxes)
Although not as flexible as using a dictionary, you might be able to get by using a simple lookup table to the map box numbers to a particular box in boxes. For example if you knew the box numbers could range 0...MAX_BOX_NUMBER, then the following would be very fast. It requires only one full scan of the Boxes list to setup the table.
MAX_BOX_NUMBER = ...
# setup lookup table
box_number = [None for i in xrange(MAX_BOX_NUMBER+1)]
for i in xrange(len(Boxes)):
box_number[Boxes[i].number] = Boxes[i]
box_number[42] # box in Boxes with given number (or None)
If the box numbers are in some other arbitrary range, some minor arithmetic would have to be applied to them before their use as indices. If the range is very large, but sparsely populated, dictionaries would be the way to go to save memory but would require more computation -- the usual trade-off.

Categories

Resources