Common Object Interface

Object API ››
Parent Previous Next

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:


If the object in question is a CvfSObj subclass, the following methods are included:

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:

Optional keys:

'flags' values

Enum values in the ensight.objs.enums module:

The ENS_ATTR_ENSVAROBJ and ENS_ATTR_ENSVARCOMP types listed below include the following additional flags values:

'type' values

Enum values in the ensight.objs.enums module:

ENS_ATTR_ENSVAROBJ type

For setting, this attribute can be:

  1. ENS_VAR object (implies magnitude)
  2. string name of the var: “velocity”
  3. variable id (implies magnitude)
  4. None

The attr will always be returned as (1) or (4).

ENS_ATTR_ENSVARCOMP type

For setting, this attribute can be:

  1. [ENS_VAR, component]
  2. ENS_VAR object (implies magnitude)
  3. ENS_ANNOT (legend object)
  4. string name + component: “velocity[X]”
  5. string name of the var: “velocity”
  6. variable id (implies magnitude)
  7. None

The attr will always be returned as (1) or (7).

ENS_ATTR_ENSPARTOBJ type

For setting, this attribute can be:

  1. ENS_PART object
  2. string name of the var: “Block - 01”
  3. part id
  4. 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):

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: 

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__))