Load existing model into python script in Maya - python

I am trying to animate an existing model I have made in Maya by using a Python script. However, I can't figure out how to access it or its polygons in order to animate them in the script. I know how to select objects beforehand but I just want to write things like this
cmds.setKeyframe( objectName, time = startTime, attribute ='rotateY', value = 0 )
where objectName is either my entire model or a specific polygon in the model

If you want to set attribute values inside your setKeyFrame call like you have shown in your code, you will have to set the attribute string appropriately. Egs. To set the y transform of a vertex attribute and keyframe it you'd do:
objectName = 'pSphere1.vtx[297]'
cmds.setKeyFrame(objectName, attribute='pnty', value=0.7)
# Where 'pnty' stands for Point Y, which is the y transform value for vertices.
Another way would be to perform all the transforms before the call to cmds.setKeyFrame() and call it with the controlPoints=True so it catches vertex and control point changes, as #theodox suggested.

Related

Abaqus python scripting: select one of two connectors which share the same coordinates

I am writing a script that automates the part of creating a model in Abaqus. In the model, two connector elements are applied to 2 nodes of two instances. The first connector starts at point a on instance 1 and ends at point b on instance 2. The second connector works vice versa. This means the two connectors obviously share the same coordinates and overlay. Picking the underlying connector with the cursor is not the problem, but automating this step and picking the underlaying connector to assign a section is where I am stuck since you cannot use the findAt() method, because it always picks the top connector. Both Wires are already implemented, but assigning a section to the second connector does not work. Can I somehow use getSequenceFromMask to access the second underlying connector?
I think it has something to do with how I can find the edges and maybe create a name or set for the edges when the wire for the connector is created. Can I specify the mask in getSequenceFromMask to be the desired edges of the second connector?
This is part of the loop. The coordinates come from libraries and the name for the section comes from a list
mdb.models['Model-1'].rootAssembly.WirePolyLine(mergeType=IMPRINT, meshable=False, points=((
mdb.models['Model-1'].rootAssembly.instances[ListeGConZug[0]].vertices.findAt((
dctX2['XKoordLLZ_%s' % ladida[i]][y], dctY2['YKoordLLZ_%s' % ladida[i]][y], dctZ2['ZKoordLLZ_%s' %
ladida[i]][y]), ),
mdb.models['Model-1'].rootAssembly.instances[ladida[i]].vertices.findAt((
dctX3['XKoordLLE_%s' % ladida[i]][y], dctY3['YKoordLLE_%s' % ladida[i]][y], dctZ3['ZKoordLLE_%s' %
ladida[i]][y]), )), ))
mdb.models['Model-1'].rootAssembly.features.changeKey(NameWire[-1],
toName=NameWire[-1])
mdb.models['Model-1'].rootAssembly.Set(edges=
mdb.models['Model-1'].rootAssembly.edges.findAt(((dctX3['XKoordLLE_%s' % ladida[i]][y],
dctY3['YKoordLLE_%s' % ladida[i]][y], dctZ3['ZKoordLLE_%s' % ladida[i]][y]), )),
name=NameWireSetConGZug[-1])
mdb.models['Model-1'].rootAssembly.SectionAssignment(region=
mdb.models['Model-1'].rootAssembly.sets[NameWireSetConGZug[-1]], sectionName=
NameWireSetConGZug[-1])
From the documentation of the WirePolyLine (Abaqus Scripting Reference Guide):
points: A tuple of point pairs, each pair being itself represented by a tuple. For part level features each point can be a Vertex, Datum point, Reference point, orphan mesh Node, or InterestingPoint object specifying the points through which the polyline wire will pass. Each point can also be a tuple of Floats representing the coordinates of a point. For assembly level features each point can only be a Vertex, Reference point, or orphan mesh Node specifying the points through which the polyline wire will pass (coordinates cannot be specified). In any of the pairs, the first or second point can be NONE. In that case, the point pair will create a zero-length wire, which is required for certain types of connectors. You must specify at least one pair.
So, you can either create a Reference point on each instance and use them to define your Wire object, either directly use Vertices (Nodes if you are using Orphan mesh).
Just a comment to your code: try to use variables and formatting, so your code is clear and easy readable. For example:
m = mdb.models['Model-1']
a = m.rootAssembly
points = (
a.instances[ListeGConZug[0]].vertices.findAt((
dctX2['XKoordLLZ_%s' % ladida[i]][y],
dctY2['YKoordLLZ_%s' % ladida[i]][y],
dctZ2['ZKoordLLZ_%s' % ladida[i]][y]),
),
#<...the rest of your points definition>
)
wire_feature = WirePolyLine(mergeType=IMPRINT, meshable=False, points=(points, )
Update for everyone searching to find a "Wire" edge when it is seems to be impossible
As presented by OP, sometimes it seems impossible to be sure that Abaqus will choose the right edge as a result of findAt method. For example, it could happen if you have several connectors at the same place and/or connectors are connecting nodes that are located at the same coordinate position. I have found a nice workaround.
When the WirePolyLine method is called it:
Creates an edge at the rootAssembly level (important!);
Creates a Feature object;
Returns the Feature object.
The Feature objects have only two members: name and id. So it is impossible to use it directly to create a Set required afterward to assign the connector section (see SectionAssignment method documentation). However, as the Wire's edge was created at the rootAssembly level, we can loop through all edges found at the required position and take the one with a good featureName!
pos = (-5., 0., 0.) # For example, this is the point where we are searching for our Wire
for edg in a.edges.findAt((pos,)):
if edg.featureName == wire_feature.name:
break

MDataHandle.setFloat() isn't changing Plug value

Using the Maya Python API 2.0, I'm trying to make a callback that changes the value of a plug. However, none of the methods I've tried are working.
I've tried using the MPlug.setFloat() method, but this didn't lead to expected results; I found no change in the plug's value. I figured this hadn't worked because I needed to clean the plug after changing its value. So, I then tried getting the plug's data handle using the MPlug.asDataHandle() method, then using the data handle's datablock() method in order to use the data handle and datablock to set the plug's value and clean it. However, I got an error saying "RuntimeError: (kFailure): Unexpected Internal Failure" upon using MDataHandle.datablock().
Now I'm trying the following, which uses the data handle to set the plug's value and clean it:
def setPlugFloatValue(node, plugName, val):
fnSet = OpenMaya.MFnDependencyNode(node)
plug = fnSet.findPlug(plugName,True)
handle = plug.asMDataHandle()
handle.setFloat(val)
handle.setClean()
The above function is intended to find a certain plug in a node, then use its data handle to set its value and clean it. In my program, the callback uses this function to change the translateX, translateY and translateZ plugs of a node's child nodes. The callback runs when the translate value of the node it's applied to changes. In a scene I'm using to test this callback, I apply the callback to a polygon mesh object, with one child which is also a polygon mesh object. So, as I translate the parent object, I expect the translate values of its child to change. But when I select the child object after translating its parent, its translate values haven't changed.
Tried your example and used setFloat() on the plug, which appears to work fine.
import maya.api.OpenMaya as OpenMaya
def setPlugFloatValue(node, plugName, val):
fnSet = OpenMaya.MFnDependencyNode(node)
plug = fnSet.findPlug(plugName,True)
plug.setFloat(val)
def applyToSelectedObjects():
sl_list = OpenMaya.MGlobal.getActiveSelectionList()
iterator = OpenMaya.MItSelectionList(sl_list)
while not iterator.isDone():
obj = iterator.getDependNode()
setPlugFloatValue(obj, "translateX", -2.0)
iterator.next()
applyToSelectedObjects()
Perhaps your issue is something else? You can also try to use setMDistance() instead, but it didn't make any difference in my testing.
distance = OpenMaya.MDistance(val)
plug.setMDistance(distance)

Why is nothing being saved when trying to update geoDjango Point objects?

SOLVED: see my own answer for an explanation.
I am writing an API to help me maintain my database when needed.
One function of my API is for updating a specific geoDjango Point field of all the concerned objects, thanks to a dedicated method of my model (which is pretty useless rergading to EDIT 1, see below).
The issue I have been facing with is the following: nothing seems to be saved to the database when calling that method on several objects, even if a call to save method seems to work while browsing the objects.
See EDIT 2, I figured out this issue was specific to geoDjango Point objects (everything else is being updated as expected).
Here below is my model:
models.py
class Location(geoModels.Model):
coord_field = geoModels.PointField(blank=True, null=True)
valTest = models.CharField(max_length=100, default="tagada") # only used to understand why coord_field can't be updated properly
def update_coord_field(self):
# some stuff is being done
self.coord_field = Point(
newLong,
newLat,
srid=3857 # I am trying to update that value
)
# nothing more
newLong and newLat are being generated thanks to Google Maps API.
As Google Maps seems to be working with SRID 3857 while GeoDjango uses SRID 4326 by default when creating a new Point, I want to force SRID of self.coord_field to SRID 3857 (I have been facing incoherent distance calculus, so I am trying to play with SRID to solve that).
Now here is my script :
script.py
from MyApp import models as MyAppModels
def update_all_coord_field():
for location in MyAppModels.Location.objects.all():
print(" old SRID : ", location.coord_field.srid)
location.update_coord_field()
location.save()
print(" new SRID : ", location.coord_field.srid)
update_all_coord_field()
But when trying to execute my script again (or simply just trying to use my new valued attribute), I can only observe that nothing was saved to the db (it still print the old value of location.coord_field.srid for all Locationobjects).
How my script is being executed:
python manage.py shell
exec(script.py)
If I call twice my script, here is the following traceback (only one object to make thinks understandable):
exec(script.py)
old SRID : 4326
new SRID : 3857 # so apparently object has been updated...
exec(script.py)
old SRID: 4326 # ...but this line proves that the updated value has not been pushed to the database
new SRID: 3857
Do you have any idea why is this not working?
FIRST EDIT: #JohnMoutafis pointed out to me there was already a function to change SRID of Point objects: transform.
Location.objects.get(pk=pk).transform(3857) # define a new value of SRID
It is usefull, yet it does not solve my issue! Please notice I did not update my code here with that transform method.
SECOND EDIT: I've been through a lot of pain to understand that it is not the save method which is not working! Actually, if I try to update "casual" data field of MyAppModel, everything works fine and is updated as expected. The real issue is only about my coord_field attribute which can't be updated, and I can't figure out why.
In my script I tried on the one hand what I explained above, and on the other hand I also tried the following to update manually (and not through a custom method of my model) coord_field:
MyAppModels.Location.objects.filter(pk=pk).update(
geoCoord=Point(location.geoCoord.x, location.geoCoord.y, srid=3857),
valTest="meat"
)
As a result, if I execute again my script, I see that valTest has been properly updated (nice!), but geoCoord still is the old value! :-( With the wrong SRID and everything. So, I think I really have a trouble updating geoDjango Point object in particular, and will update the title of my question as a consequence of that!
Based on this gis.stackexchange post, I will assume that you are declaring each point initially without an explicit srid, therefore GeoDajngo assumes that you must have declared it on 4326.
You need to declare every point in your database explicitly.
Here is a script to help you do that with your existing points.
for item in Location.objects.all():
item.coord_field = Point(item.coord_field.x, item.coord_field.y, srid=4326)
item.save()
You can use transform instead of your custom method to change the srid of your Points:
Location.objects.all().transform(3857)
EDIT due to comment:
Since clarification provided by #KrazyMax on the comments, he needs to calculate the spherical distance. I have made a post about that here: How to calculate 3D distance (including altitude) between two points in GeoDjango
SOLVED: my conlusion is that once you specified SRID of one Point, you can not change it later. Even the transform method won't change SRID, but will only make some conversion in place, without altering original SRID of your Point. Actually, I think that when I write:
coord_field = geoModels.PointField(blank=True, null=True)
the default 4826 SRID is already set once for all to the appropriate column of the database (and only this SRID can then be used for this column). So, even when I write:
self.coord_field = Point(newLong, newLat, srid=newSRID)
I won't be able to associate another SRID to my Point. Some calculus is going to be computed, but at the end the Point attribute will still be affected of the initial SRID.
To be clear: I am not sure 100% about those conclusions (I did not find any appropriate doc), but deleting coord_field column then setting coord_field as follows:
coord_field = geoModels.PointField(srid=3857, blank=True, null=True)
..did the trick. If anyone has got some clean answer for me, I would still appreciate to read it!

matplotlib figure canvas name

I have a GUI built using PyQt4 where I use matplotlib (FigureCanvas) to plot several plots using multiple canvases (this is intentional rather than using subplots). for each canvas I use method:
self.canvas_xx.mpl_connect('scroll_event', self.on_mouse_scroll)
where xx represents the iteration of the canvas to get a signal to perform some action. I want to be able to reference the canvas by its name rather than using:
ylabel = event.inaxes.axes.get_ylabel().split(' ')[0]
where I use the longer method of referncing the ylabel name of each graph.
I looked under the event method using: dir(event) and there is a method called "canvas", but there is no apparent method to get the name of the canvas.
Any ideas?
I am not quite sure what you mean by name, but you can get a a reference to the canvas object via
event_canvas = event.inaxes.axes.figure.canvas
and canvas events are hashable, so you can use them as keys in a dict
other_thing = pre_populated_dict[event_canvas]
to keep track of what ever other data you want to about a given canvas in your code.
In python there is, in general, not a clean way to get the names of references to an object (it can be done, but you shouldn't).

Python: KeyError 'shift'

I am new to Python and try to modify a pair trading script that I found here:
https://github.com/quantopian/zipline/blob/master/zipline/examples/pairtrade.py
The original script is designed to use only prices. I would like to use returns to fit my models and price for invested quantity but I don't see how do it.
I have tried:
to define a data frame of returns in the main and call it in run
to define a data frame of returns in the main as a global object and use where needed in the 'handle data'
to define a data frame of retuns directly in the handle data
I assume the last option to be the most appropriate but then I have an error with panda 'shift' attribute.
More specifically I try to define 'DataRegression' as follow:
DataRegression = data.copy()
DataRegression[Stock1]=DataRegression[Stock1]/DataRegression[Stock1].shift(1)-1
DataRegression[Stock2]=DataRegression[Stock2]/DataRegression[Stock2].shift(1)-1
DataRegression[Stock3]=DataRegression[Stock3]/DataRegression[Stock3].shift(1)-1
DataRegression = DataRegression.dropna(axis=0)
where 'data' is a data frame which contains prices, stock1, stock2 and stock3 column names defined globally. Those lines in the handle data return the error:
File "A:\Apps\Python\Python.2.7.3.x86\lib\site-packages\zipline-0.5.6-py2.7.egg\zipline\utils\protocol_utils.py", line 85, in __getattr__
return self.__internal[key]
KeyError: 'shift'
Would anyone know why and how to do that correctly?
Many Thanks,
Vincent
This is an interesting idea. The easiest way to do this in zipline is to use the Returns transform which adds a returns field to the event-frame (which is an ndict, not a pandas DataFrame as someone pointed out).
For this you have to add the transform to the initialize method:
self.add_transform(Returns, 'returns', window_length=1)
(make sure to add from zipline.transforms import Returns at the beginning).
Then, inside the batch_transform you can access returns instead of prices:
#batch_transform
def ols_transform(data, sid1, sid2):
"""Computes regression coefficient (slope and intercept)
via Ordinary Least Squares between two SIDs.
"""
p0 = data.returns[sid1]
p1 = sm.add_constant(data.returns[sid2])
slope, intercept = sm.OLS(p0, p1).fit().params
return slope, intercept
Alternatively, you could also create a batch_transform to convert prices to returns like you wanted to do.
#batch_transform
def returns(data):
return data.price / data.price.shift(1) - 1
And then pass that to the OLS transform. Or do this computation inside of the OLS transform itself.
HTH,
Thomas

Categories

Resources