Common Object Interface
Command language generation from the Object API
By default, the object API does not emit any command language, but we would provide controls to allow it (very much like the native API situation). The core code that attempts to record command language for portions of command language that can be naturally generated from the internal command language tables (not all of the API can be generated this way). We have exposed the ability to control that mechanism.
How does it work? Well, there are multiple methods that can be used to set an attribute:
foo.BAR = value
foo.setattr(BAR, value [,record=state])
foo.setattrs({BAR: value} [,record=state])
group.setchildattr(BAR, value [,record=state])
group.setchildattrs({BAR: value} [,record=state])
The latter 4 cases all allow for the recording state to be specified explicitly for the scope of that call. The first
case (foo.BAR = value) works as if the keyword record=MODEL_RECORD_CMD_LANG_GLOBAL was set. If the 'record' keyword is not set on the other four cases, it also defaults to MODEL_RECORD_CMD_LANG_GLOBAL.
The allowed state values:
ensight.objs.enums.MODEL_RECORD_CMD_LANG_OFF - explicitly do not record
ensight.objs.enums.MODEL_RECORD_CMD_LANG_ON - explicitly record
ensight.objs.enums.MODEL_RECORD_CMD_LANG_HONOR - honor the cmdlang 'Recrdr' setting
ensight.objs.enums.MODEL_RECORD_CMD_LANG_GLOBAL - use the object API global setting which can be any of the above 3 values
The object API global is:
ensight.objs.core.OBJECT_API_JOURNAL
and it defaults to: MODEL_RECORD_CMD_LANG_OFF
In general, please avoid using OBJECT_API_JOURNAL as it is a global and its scope can be difficult to control. Use the record= keywords if you can. If you must use OBJECT_API_JOURNAL, make sure you reset the state back to what it was before you started when you are finished to ensure it does not get stuck into the state you placed it in.
The Common Object API Interface
The object interface is based on thin, dynamically generated Python wrappers around the core CvfObj/CvfSObj C++ objects. The objects export the ‘attr’ interface along with other methods. The core wrappers implement the following methods/members for all objects:
- CvfObjID = foo.__objid__ (ro)
- list = foo.__methods__ (ro)
- list = foo.__members__ (ro) (a list of all of the attributes in string form)
- list = foo.__ids__ (ro) (a list of all of the attributes in number form)
- list = foo.__class__ (ro) (the object class as a string: ENS_CASE, ENS_GLOBALS, ENS_LPART, ENS_MAT, ENS_PART, ENS_SPEC, ENS_VAR)
- foo.ATTR = v (set the attribute to a value)
- v = foo.ATTR (get an attribute value)
- foo.setattr(”attrname”,value) (Note: for attributes that support the concept of “DEFAULT”, one can pass enums.DEFAULT_VALUE which is -1.2345e-10)
- foo.setattr(attrnumber,value) (preferred form using the numerical form of the attribute ID)
- foo.setattr(attrnumber) (if no “value” is provided, EnSight will call all the callbacks for that attribute as if the value has been changed, but the value of the attribute has not changed. This is generally used in the core for special purposes.)
- foo.setattrs(dict[,all_errors=1]) (set a collection of attributes from a dictionary, keys can be attribute names as string or numbers) If all_errors is 1, the routine will abort on any attribute set failure and will throw an exception. If all_errors is 2, the routine will try to set all of the attributes in the dict and throw an error if any of the attribute sets failed.
- v = foo.getattr(”attrname”)
- v = foo.getattr(attrnumber)
- dict = foo.getattrs(attrlist[,text=1]) (get a collection of attributes as a dictionary using keys in attrlist, strings or numbers. If text is set, the dict keys will be strings instead of numbers)
- dict = foo.getattrs([text=1]) (same as: dict = foo.getattrs(foo.ids[,text=1) )
- dict = foo.attrinfo(attr|attrlist[,text=1]) This returns a dictionary using the attributes as keys. The value for the key is a dictionary with a collection of string keys, including: ‘name’, ‘type’, ‘basetype’, ‘desc’, ... See cvf_attrs.h for an interpretation of these fields. The enums for interpreting this dict (struct) are in ensight.objs.enums (e.g. CVF_ATTR_STRING, CVF_ATTR_FLOAT, ...) Some attributes will have a ‘dependencies’ key which points to a list of dictionaries, one for each entry in the ‘deplist’ array (see: cvf_attrs.h and details below).
- bool = foo.attrissensitive(attr|”attr”) This checks any sensitivity dependencies on an attribute to see if modification of the attribute should be allowed in the GUI. It returns True if the attr is ok to be edited, False if not and it throws an exception if the attr is invalid. If there are no dependencies set for the attr, it always returns True. It is legal to pass the enum or a string, but the enum is much faster.
- treenode = foo.attrtree([all=a][filter=f][exclude=e][include=i][group_exclude=ge][group_include=gi]) Return a suggested hierarchy for the display of attributes by categories. See the complete description and example later on this page.
- num = foo.setattr_begin()
- num = foo.setattr_end()
- num = foo.setattr_status() If an application knows that it will be setting a number of attributes, it can inform the object system by calling begin() before the first one and end() after the last one. The object system may suppress redundant events, command language generation, etc between the pairs. The routines return a number that increments with begin() and decrements with end(). status() returns the current value and if > 0 the object should consider itself to be setting bulk attributes. Note: the setattrs() method brackets all of its operations in begin()/end() calls automatically.
- foo.setmetatag(”tag”[,value]): This method allows the caller to change the value of a tag. The name of the tag to change is passed as a string. If no value is specified, the tag is removed. If a value is specified, the tag is replaced (if it already exists) or added. Values can be simple int, float or string values.
- foo.hasmetatag("tag"): This method checks to see if the tag "tag" exists. If so, it returns True, otherwise False.
- foo.getmetatag("tag"): This method returns the value of the specific tag. It is an error if the tag is undefined.
If the object in question is a CvfSObj subclass, the following methods are included:
- foo.pyref() (allows Python to take a reference to the object. This will not allow the object to be destroyed until the Python wrapper is and it improves performance as the object hash value can be cached)
- foo.pyunref() (releases the previous cache)
Note: the Python ‘compare’ operation is implemented for the proxy objects as a comparison of the CvfObj IDs the objects wrapper. If two proxy objects are proxies to the same CvfObj ID, they will compare as the same.
Attrinfo() dictionary
The output of the attrinfo() function is illustrated by the following example:
print(core.parts[0].attrinfo(enums.VISIBLE))
{'name': 'VISIBLE', 'basetype': 0, 'numvals': 1, 'flags': 0, 'type': 0, 'desc': 'Visible'}
A dictionary is returned for each attribute. Conceptually, each attr has a ‘type’. This is the high-level type of an attribute. There also exists a ‘basetype’ which is the lower-level representation of the attr. For example, a high level type might be “RGB color” while the lower level type would be “floats”. In most situations, applications should use the ‘type’ wherever possible and only revert to using ‘basetype’ if the ‘type’ is unknown. The dictionary includes the ‘name’ of the attr along with a text description. The ‘name’ must be a legal Python variable name while the description should be used to fill in GUI elements, etc.
The dictionary returned by attrinfo() will always have the following keys:
- ‘type’: the high-level type. This can include any of the values noted in the next section
- ‘basetype’: the low-level type. Only types starting with ‘CVF’ are allowed.
- ‘numvals’: the number of values of the ‘basetype’.
- ‘desc’: string description for the attr
- ‘name’: the python legal name for the attr
- ‘flags’: this integer is formed by or-ing values from the table below
Optional keys:
- ‘range’: if selected in the flags, this key will be a list of two floats or ints: [min,max]. see ‘flags’ below
- ‘enums’: if selected in the flags, this key will be list of lists. Each list has three values: [int_value, description, python_name]. The integer value is the number for the enum (see ‘flags’ below), while the other two values are human and Python strings. The python_name will also be available as an integer in the ensight.objs.enums module.
- ‘dependencies’: if present, this key is a list of dictionaries that describe the fact that this attr is dependent on another attr. The dictionary is described below.
'flags' values
Enum values in the ensight.objs.enums module:
- CVF_ATTR_HAS_RANGE - The ‘range’ key will be present.
- CVF_ATTR_NO_MAXIMUM - If CVF_ATTR_HAS_RANGE is set, the max value is unbounded.
- CVF_ATTR_NO_MINIMUM - If CVF_ATTR_HAS_RANGE is set, the min value is unbounded.
- CVF_ATTR_LESS - If CVF_ATTR_HAS_RANGE is set, the max value is strictly less than the range max.
- CVF_ATTR_GREATER - If CVF_ATTR_HAS_RANGE is set, the min value strictly greater than the range min.
- CVF_ATTR_HAS_ENUMS - The ‘enums’ key will be present.
- CVF_ATTR_BIT_FLAGS - If the type is CVF_ATTR_ENUM, then the enum values are formed by or-ing values from the ‘enums’ key. If not set, only the specific values in the ‘enums’ key should be used.
- CVF_ATTR_READONLY - If set, the attr is read-only (setattr will fail).
The ENS_ATTR_ENSVAROBJ and ENS_ATTR_ENSVARCOMP types listed below include the following additional flags values:
- ENS_ATTR_VAR_FILTER - if this bit is set, one or more of the next bits will be set that describe the allowed variable types for this attribute
- ENS_ATTR_VAR_SCALAR
- ENS_ATTR_VAR_VECTOR
- ENS_ATTR_VAR_CONST
- ENS_ATTR_VAR_TENSOR
- ENS_ATTR_VAR_COORD
- ENS_ATTR_VAR_TIME
- ENS_ATTR_VAR_SCLCOMPLEX
- ENS_ATTR_VAR_VCTCOMPLEX
- ENS_ATTR_VAR_NODE
- ENS_ATTR_VAR_ELEM
- CVF_ATTR_USERFLG_MASK - this bitmask will include all of the above bits
'type' values
Enum values in the ensight.objs.enums module:
- CVF_ATTR_BOOL - boolean
- CVF_ATTR_COLOR_RGB - 3 floats
- CVF_ATTR_COLOR_RGBA - 4 floats
- CVF_ATTR_ENUM - integer
- CVF_ATTR_INT - integer
- CVF_ATTR_FLOAT - float
- CVF_ATTR_STRING - string
- CVF_ATTR_OBJPTR - (a basetype only)
- CVF_ATTR_SOBJPTR - ensight object reference (list)
- ENS_ATTR_ENSVAROBJ - ensight ENS_VAR object
- ENS_ATTR_ENSVARCOMP - ensight ENS_VAR object + component. A list: [object, component_int]
- ENS_ATTR_ENSLEGENDOBJ - ensight ENS_ANNOT object (legend)
- ENS_ATTR_ENSPARTOBJ - ensight ENS_PART object
- ENS_ATTR_ENSPLOTOBJ - ensight ENS_PLOTTER object
- ENS_ATTR_PYDICTLIST - a Python list of dictionaries
- ENS_ATTR_PYDICTIONARY - a Python dictionary
- ENS_ATTR_MATRIX44
- ENS_ATTR_QUATERNION
- ENS_ATTR_PALETTEOBJ - ENS_PALETTE object
- ENS_ATTR_TEXTUREOBJ - ENS_TEXTURE object
- ENS_ATTR_EMITTERLIST - a list of ens_emitterobj objects (see ens_emitterobj below)
ENS_ATTR_ENSVAROBJ type
For setting, this attribute can be:
- ENS_VAR object (implies magnitude)
- string name of the var: “velocity”
- variable id (implies magnitude)
- None
The attr will always be returned as (1) or (4).
ENS_ATTR_ENSVARCOMP type
For setting, this attribute can be:
- [ENS_VAR, component]
- ENS_VAR object (implies magnitude)
- ENS_ANNOT (legend object)
- string name + component: “velocity[X]”
- string name of the var: “velocity”
- variable id (implies magnitude)
- None
The attr will always be returned as (1) or (7).
ENS_ATTR_ENSPARTOBJ type
For setting, this attribute can be:
- ENS_PART object
- string name of the var: “Block - 01”
- part id
- None
The attr will always be returned as (1) or (4).
ENS_ATTR_EMITTERLIST type
<TBD>
'dependencies' values
Each dependency dictionary has three keys: ‘flags’, ‘attrib’ and ‘value’. ‘attrib’ is the enum of another attribute that this attribute is dependent on. ‘flags’ is an integer formed by or-ing the following values (ensight.objs.enums):
- CVF_ATTRDEP_SENS_VALUE - the only currently exposed type of dependency. Basically, it says that this attr should only be ‘sensitive’ if the value of the attr specified by ‘attrib’ has some relationship to the ‘value’ key. The other CVF_ATTRDEP_SENS flags specify the specific dependency
- CVF_ATTRDEP_SENS_NOT
- CVF_ATTRDEP_SENS_OR
- CVF_ATTRDEP_SENS_AND
- CVF_ATTRDEP_SENS_XOR
- CVF_ATTRDEP_SENS_EQ
- CVF_ATTRDEP_SENS_GT
- CVF_ATTRDEP_SENS_LT
Object.attrtree() Method
This method is on MOST of the intrinsic objects, but not all of them. In the future, it is expected that it will be available on all core objects. This method is used to generate a "visual" tree of an object's attributes. The most common use would be to dynamically generate labels and hierarchy for a "property sheet" editor.
foo.attrtree([all=a][filter=f][exclude=e][include=i][group_exclude=ge][group_include=gi][insensitive=s])
The method returns an object tree that describes the way attribute should be laid out. Each object has three attributes: ‘attr’, 'hasdeps' and ‘name’. All objects will have names and group objects will have an attr of -1. All objects can also be iterated for children objects of the same type. Only objects with an ‘attr’ of -1 will have children. len() can be used to get the number of child objects. The top level object always has the name ‘root’. The 'hasdeps' attribute is the number of attributes in this attrtree() that have a dependency on this attr. This can be used to decide if a new sensitivity check is needed if a given attribute changes.
There are some optional keyword parameters:
- all=1 will include all attrs for the object, even if they are not in the group tables.
- filter=[objlist] this keyword should be set to a list of EnSight objects. The output of the attrtree() method will be filtered to include only the attributes in common between all of the objects (they do not need to be of the same type).
- include=[list] this keyword should be set to a list of attribute enums. Only the enums in the list will be included in the output object tree. Note: passing an empty list via this keyword, all of the enums will be initially excluded. This is useful with the group_include= keyword.
- exclude=[list] this keyword should be set to a list of attribute enums. Any enums in the list will be removed from the output object tree.
- group_exclude=[list] this keyword should be set to a list of attribute enums. For any enum in this list, exclude the enum and all the other enums in the same groups as the passed enums. Think of this as a shortcut for exclude= that requires you to only pass a single enum in the group you want to suppress.
- group_include=[list] this keyword should be set to a list of attribute enums. For any enum in this list, include the enum and all the other enums in the same groups as the passed enums. Think of this as a shortcut for include= that requires you to only pass a single enum in the group you want to include. Note: it may be necessary to pass include=[] (the empty list) to start with an empty list of enums.
- insensitive=s if this keyword is set to 0, attrtree() will call foo.issensitive() on each filtered attr and if the attr is not currently sensitive, it will remove it from the output. The default value for this keyword is 1 which disables all sensitivity filtering.
Example using the attrtree()/attrinfo() calls:
from ensight.objs import *
def walk_tree(part,obj,s):
a = obj.attr
if (a == -1):
print("{}Group={}".format(s, obj.name)))
else:
info = part.attrinfo(a)
t = enum_to_name(a)
d = info['desc']
print("{}Attr={} - '{}' ({:d} deps)".format(s, t, d, obj.hasdeps))
for i in obj:
walk_tree(part,i,s+" ")
walk_tree(core.PARTS[0],core.PARTS[0].attrtree(),"")
Object ID Example
A commonly asked question in EnSight automation is: "how do I find the last N created objects of a specific type?". For example, one has a script that calls some unknown code that creates some parts. How does one figure out what parts where created?
This functionality has been implicitly built into the object system via the object IDs. The object IDs are monotonically increasing integers that start a 1 for a session. They are never re-used. In Python, this is the 'CvfObjID' that shows up in the 'print' output, and it is also the '__objid__' attribute. The following will return the most recently created 4 parts:
from ensight.objs import *
from operator import attrgetter
plist = sorted(core.PARTS,key=attrgetter('__objid__'))[-4:]
Basically, take the partlist, sort it by __objid__ and return the last 4 values. Pretty useful, but it would be even better if you did not need to know in advance how many objects were created. In 10.0.2(f) and later releases, ensight.objs.next_id() returns the CvfUID for the next object that will be created by EnSight. If one grabs this number, does something and then looks at the target list for objects with IDs >= that number gets you the new objects. Wrapped up as a function:
from ensight.objs import *
from operator import attrgetter
# define a class with a custom __cmp__ method
class greaterthanequal():
def __init__(self, value):
self._v = value
def __cmp__(self,other):
if (other >= self._v):
return 0
return 1
def find_recent_objs(objlist=core.PARTS,count=1,after_id=None):
if (after_id == None):
tmp = objlist
else:
f = {'__objid__': greaterthanequal(after_id) }
tmp = core.find_objs(objlist,filter=f)
if (count == None):
return tmp
return sorted(tmp,key=attrgetter('__objid__'))[-count:]
By default, it looks at the partlist, returning the last part created, but one may filter the list by a specific ID (after or equal to that ID). For example, if one wanted the annotations created by a segment of code:
startid = ensight.objs.next_id()
# ... do something ...
newannots = find_recent_objs(objlist=core.ANNOTS,count=100000,after_id=startid)
to get a list of the annotation objects (if any) created between the 'next_id()' and 'find_recent_objs()' calls.
Get the last object created:
For 10.0.3(a) and later, simply get the max of the list of objects and the max operator uses the object IDs. Objects created later have larger IDs, thus this is a quick (and simple) way of getting the last created object.
print(max(core.PARTS))
print(max(core.QUERIES))
print(max(core.VARIABLES))
etc.
Prior to 10.0.3(a):
print(max(core.PARTS,key=lambda x: x.__objid__))