OpenMaya API(python):从MObjects打印更多有用的信息?例如。 MFloatVector(x,y,z)

时间:2013-11-22 20:24:28

标签: python maya

使用常规python对象,很容易看到它们的细节。 E.g。

vec = (1, 2, 3)
print vec

=>

(1, 2, 3)

使用等效的OpenMaya(OM)对象时,所有print或str()显示都是对象的类型:

vec = OpenMaya.MFloatVector(1,2,3)
print vec

=>

<maya.OpenMaya.MFloatVector; proxy of <Swig Object of type 'MFloatVector *' at 0x000000002A346060> >

是否有一般方法要求MObject提供更多详细信息? 我想得到一个结果:

MFloatVector(1, 2, 3)

----- 编辑 -----

从C ++文档中,我看到我想要的信息可以通过ostream&lt;&lt;在C ++中获得。运营商。例如。对于MFloatVector的&lt;&lt;它说:

  

使用的格式是[x,y,z]

另一种问我问题的方法是:在python中,如何在内存中创建一个ostream,将对象发送给它,然后将结果作为字符串获取?

----- 编辑#2 -----

我的导入声明是:

import maya.OpenMaya as OpenMaya

这意味着我使用的是Maya Python API的第1版。 (因为某些版本2的东西是存根,例如MGlobal。我正在考虑使用这些版本1的功能,所以我留在版本1。)

我发布了自己的答案,即使用版本2,以获得所需的行为。 TBD是否版本2具有所需的一切,以及将版本1示例转换为版本2所需的内容。目前,我坚持使用版本1.如果有人有办法让版本1提供更有用的打印细节,那就是我会接受什么作为答案。

3 个答案:

答案 0 :(得分:1)

Maya Python API的第2版对python更友好。

要访问版本2,请从

更改导入语句
import maya.OpenMaya as OpenMaya

import maya.api.OpenMaya as OpenMaya

这将需要更改脚本,因为许多方法被调整为更加python友好。

一旦完成,“print vec”和“str(vec)”和“len(vec)”都将成为有用的操作:

vec = OpenMaya.MFloatVector(1, 2, 3)
print vec
print str(vec)
print len(vec)

=&GT;

(1, 2, 3)
(1, 2, 3)
3

答案 1 :(得分:1)

用你自己包装repr函数肯定是可能;类似的东西:

import maya.OpenMaya as om

def repr_MfloatVector(self):
    n = self.__class__.__name__
    return "%s(%r, %r, %r)"%(n, self[0], self[1], self[2])

om.MFloatVector.__repr__ = repr_MfloatVector

vec = om.MFloatVector(1,2,3)
print vec

这将影响所有未来(但不是过去)的MFloatVectors。但是,为什么你会遇到这样的麻烦是另一回事。至于MObjects的一般工作在这个层面上做了太多工作。但是你可以尝试询问每个对象的 obj.length(),以确定它是否可迭代等等,这样你就可以得到一个相当好的传播但仍有太多的工作没有多少收益。

答案 2 :(得分:0)

这是定义“Repr(self)”的代码。一个全局函数,为大多数对象返回“repr(self)”。但是对于返回以“&lt;”开头的表示的对象,结果是一个成员值列表,(1)不是内部的(不以'__'开头),而(2)不是方法。

E.g。 OpenMaya MFloatVector实例“OpenMaya.MFloatVector(1,2,3)”给出结果:

#MFloatVector( x: 1.0, y: 2.0, z: 3.0)

代码:

# ==================== AttributeAccess.py ====================
import maya.cmds as cmds
import maya.mel as mel
import sys
import maya.OpenMaya as OM        # Version 1
import math

import inspect
import types

# ---------- Common Stuff ----------

# "something" can be any Python object.
def Exists(something):
    return something is not None

def printElements(ob):
    print '----- Elements: -----'
    i = 0
    for x in ob:
        print ' [' + str(i) + ']:  ' + repr(x)
        i += 1
    print '---------------------'

def printDictElements(ob):
    print ''
    print '-----------------------'
    for x in ob: print repr(x) + ':  ' + repr(ob[x])
    print '-----------------------'


# ---------- inspect Attributes ----------

# NOTE: ob is an instance, NOT a type object.
def TypeName(ob):
    return ob.__class__ .__name__

# Excludes 'internal' names (start with '__').
def Public(name):
    return not name.startswith('__')

# member is element of inspect.getmembers:
#   a two-element tuple.
def MemberWithType(member):
    return ( member[0], TypeName(member[1]), member[1] )
#print MemberWithType( (1.1, 2) )

def Members(ob):
    return inspect.getmembers(ob)

# True for Maya Python's 'this' member.
# member [1] is attribute value.
def SwigThis(member):
    return (member[0] == 'this') and (TypeName(member[1]) == 'SwigPyObject')

# HACK: "not SwigThis": omit Maya Python's 'this' member.
def PublicMembers(ob):
    members = filter(lambda member: Public(member[0]) and not SwigThis(member), Members(ob))
    return map(MemberWithType, members)

# Excludes 'internal' names (start with '__').
def Dir(ob):
    return filter(Public, dir(ob))


def _Type_And_Features(ob, names):
    return '{0}.({1})'.format(TypeName(ob), ', '.join(names))

def MemberName(member):
    return member[0]

# member with typename inserted as [1]. So descriptor is [2].
# member type-name is [1].
def CallableMember(member):
    #return (member[2].__class__  is types.MethodType)
    return inspect.isroutine(member[2])

def MemberNames(members):
    return map(MemberName, members)

def Features(ob):
    return _Type_And_Features(ob, MemberNames(PublicMembers(ob)) )   
    #return _Type_And_Features(ob, Dir(ob))

def Callable(ob):
    return _Type_And_Features(ob, MemberNames(filter(lambda a: CallableMember(a), PublicMembers(ob))))
    #return _Type_And_Features(ob, filter(lambda a: callable(a), Dir(ob)))

def IsClassVar(self, attrName):
    return hasattr(self.__class__, attrName)

# REQUIRE attrName already known to be supported by self.
# But just in case, return False if exception, so will be skipped.
def IsNotSameAsClassVar(self, attrName):
    try:
        if not IsClassVar(self, attrName):
            return True
        # If it has different value than class' attribute, it is on the instance.
        return getattr(self, attrName) is not getattr(self.__class__, attrName)
    except:
        return False

# ---------- _MayaValues ----------

# NOTE: 'ob' is an instance, not the class (type) itself.
def _ClassVars(ob):
    attributes = filter(lambda a: not CallableMember(a), PublicMembers(ob))
    # Keep class variables.
    # "not IsProperty": HACK: Skip Maya/Swig 'property' class variables.
    classVars = filter(lambda desc: IsClassVar(ob, desc[0]) and not IsProperty(getattr(ob.__class__, desc[0])), attributes)
    return MemberNames(classVars)

# NOTE: 'ob' is an instance, not the class (type) itself.
def ClassVars(ob):
    return _Header_And_Values(TypeName(ob) + ' Class_Variables',
        map(lambda attr: attr + ': ' + Repr(getattr(ob, attr)), _ClassVars(ob)),
        0
        )


# If it is invocable without parameters, return (attrName, typename, result of invocation).
# if Not reportExceptions, return None for Exception.
def CallAttribute_AsTriple(self, attrName, reportExceptions=False):
    try:
        expressionString = 'self.{0}()'.format(attrName)
        result = eval(expressionString)
        typename = TypeName(result)
    except Exception as e:
        if reportExceptions:
            result = e
            typename = '*** Exception'
        else:
            return None
    return (attrName, typename, result)

# member is tuple (attrName, typeName, value)
# If it is invocable without parameters, return (attrName, typename, result of invocation).
# if Not reportExceptions, return None for Exception.
def CallMember_AsTriple(self, member, reportExceptions=False):
    return CallAttribute_AsTriple(self, member[0], reportExceptions)

# If it is invocable without parameters, return string: pretty-printed result of invocation.
# if Not reportExceptions, return None for Exception.
def CallAttribute(self, attrName, reportExceptions=False):
    try:
        #printElements(locals())
        expressionString = 'self.{0}()'.format(attrName)
        #print Eval(expressionString, locals())
        result = eval(expressionString)
        resultString = Repr(result)
        typename = TypeName(result)
    except Exception as e:
        if reportExceptions:
            #result = '*** Exception  ' + str(e)
            result = e
            resultString = str(e)
            typename = '*** Exception'
        else:
            return None
    return ' .{0} {{{1}}}= {2}'.format(attrName, typename, resultString)

# member is tuple (attrName, typeName, value)
# If it is invocable without parameters, return string: pretty-printed result of invocation.
# if Not reportExceptions, return None for Exception.
def CallMemberRepr(self, member, reportExceptions=False):
    return CallAttribute(self, member[0], reportExceptions)

def FirstLine(string):
    lines = string.split('\n')
    if len(lines) > 1:
        return lines[0] + '...'
    return string

def ArgLessRoutines_AsTriples(ob):
    members = PublicMembers(ob)
    members_WithNones = map(lambda member: CallMember_AsTriple(ob, member), members)
    # member is tuple (attrName, typeName, value)
    members = filter(Exists, members_WithNones)
    return members

def ArgLessRoutines(ob):
    members = PublicMembers(ob)
    members_WithNones = map(lambda member: CallMember_AsTriple(ob, member), members)
    # member is tuple (attrName, typeName, value)
    members = filter(Exists, members_WithNones)
    resultStrings = map(lambda string: FirstLine(string), resultStrings)
    return _Header_And_Values(TypeName(ob) + ' ArgLessRoutines', resultStrings)

def _MayaCallables_Common(mayaType):
    try:
        typeName = mayaType.__name__
        if typeName == 'MDagPath':
            return ['fullPathName']
        if typeName == 'MTypeId':
            return ['id']
        if typeName == 'MFnMesh':
            return ['numPolygons', 'numVertices', 'numEdges', 'numFaceVertices']
        if typeName == 'MDagPath':
            return ['fullPathName']
    except Exception as e:
        print e
    return []

def _MayaCallables_Version1(mayaType):
    return _MayaCallables_Common(mayaType)

def _MayaCallables_Version2(mayaType):
    return _MayaCallables_Common(mayaType)

# Names of callable attributes to include in Repr of 'ob'.
# For instances of types in 'maya.OpenMaya'.
def MayaCallables(ob):
    try:
        typ = ob.__class__
        if typ == type:
            return []
        if typ.__module__ == 'maya.OpenMaya':
            return _MayaCallables_Version1(typ)
        if typ.__module__ == 'OpenMaya':
            return _MayaCallables_Version2(typ)
    except Exception as e:
        print e
    return []

# Return (name, typename, value) per maya callable.
def _MayaValues(ob):
    callables = MayaCallables(ob)
    members_WithNones = map(lambda attrName: CallAttribute_AsTriple(ob, attrName), callables)
    members = filter(Exists, members_WithNones)
    return members

# TODO: If all results fit on single line, remove "{typename}" so is more readable.
#def MayaValues(ob):
#    resultStrings = _MayaValues(ob)
#    return _Header_And_Values(TypeName(ob) + ' MayaValues', resultStrings)

# ---------- Attributes ----------
def _AttributeNames(ob):
    attributes = filter(lambda a: not CallableMember(a), PublicMembers(ob))
    # Omit class variables.
    attributes = filter(lambda desc: IsNotSameAsClassVar(ob, desc[0]), attributes)
    return MemberNames(attributes)

def AttributeNames(ob):
    return _Type_And_Features(ob, _AttributeNames(ob))
    #return _Type_And_Features(ob, filter(lambda a: not callable(a), Dir(ob)))

def _Header_And_Values(headerString, valueStrings, maxWidth=100):
    if sum(map(len, valueStrings)) > maxWidth:
        # pretty print, with one value per line.
        return '{0}(\n  {1}\n)'.format(headerString, '\n  '.join(valueStrings))
    return '{0}({1})'.format(headerString, ', '.join(valueStrings))

def _Type_And_Values(ob, valueStrings, maxWidth=100):
    return _Header_And_Values(TypeName(ob), valueStrings, maxWidth)

def AttributeValues(ob):
    return _Type_And_Values(ob, map(lambda attr: str(getattr(ob, attr)), _AttributeNames(ob)))

def Attributes(ob, depth=0):
    # Limit recursion.
    # If deep, don't include MayaValues.
    if depth >= 2:
        return _Type_And_Values(ob, map(lambda attr: attr + ': ' + str(getattr(ob, attr)), _AttributeNames(ob)))
    attributes = map(lambda attr: attr + ': ' + Repr(getattr(ob, attr), depth + 1), _AttributeNames(ob))
    if depth == 0:
        mayaValues = _MayaValues(ob)
        if len(mayaValues) > 0:
            for mayaValue in mayaValues:
                attribute = mayaValue[0] + ': ' + Repr(mayaValue[2])
                attributes.append(attribute)
    return _Type_And_Values(ob, attributes)

def IsProperty(ob):
    return (TypeName(ob) == 'property')

# ---------- Repr ----------
def Repr(ob, depth=0):
    r = repr(ob)
    # Helps avoid undesired recursion.
    if ob.__class__ == type:
        return r
    if (r.__class__ == types.StringType) and (len(r) > 0) and (r.find('<') <> 0):
        # Has a good repr.
        return r
    # Doesn't have a good repr; inspect it instead.
    return '#' + Attributes(ob, depth)

def Eval(expressionString, _locals=locals(), _globals=globals()):
    return str(expressionString) + "= " + str(Repr(eval(expressionString, _globals, _locals)))


# ---------- Testing ----------

# ---------- class Vector ----------
class Vector(object):
    def __init__(self, x=0.0, y=0.0, z=0.0):
        self.x, self.y, self.z = x, y, z
    # Provide useful info for 'repr(self)', 'str(self)', and 'print self'.
    def __repr__(self):
        return 'Vector({0}, {1}, {2})'.format(self.x, self.y, self.z)
    # math operators
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
    # ==
    def __eq__(self, other):
        return (self.__class__ == other.__class__) and \
            (self.x == other.x) and \
            (self.y == other.y) and \
            (self.z == other.z)
    # a simple method
    def ApproximateLength(self):
        return self.x + self.y + self.z
    # list/sequence/iterator support.
    def tolist(self):
        return [self.x, self.y, self.z]
    def __len__(self):
        return 3
        # No need for "next(self)", because we create a list, use its iterator.
    def __iter__(self):
        return iter(self.tolist())
# class variable
Vector.Zero = Vector()

# ---------- inspecting Vector ----------
def Testing_Vector_Attributes():
    #vec = (1, 2, 3)
    #vec = [1, 2, 3]
    #vec = Vector(1.0, 2.0, 3.0)
    vec = OM.MFloatVector(1, 2, 3)
    print vec
    #for x in vec: print x
    print dir(vec)
    print TypeName(vec)
    print Dir(vec)
    print Features(vec)
    print Callable(vec)
    print '-----------------------'
    printElements(PublicMembers(vec))
    print '-----------------------'
    print AttributeNames(vec)
    #print vec.x
    #print eval('vec.x')
    #print getattr(vec, 'x')
    print AttributeValues(vec)
    print Attributes(vec)
    vec = OM.MFloatVector(1, 2, 3)
    #print repr(vec)
    #print Repr('Hi')
    print Repr( (1,2,3) )
    print Repr(vec)
    print ClassVars( Vector(1.0, 2.0, 3.0) )
    print ClassVars( OM.MFloatVector(1, 2, 3) )
    print Eval('OM.MMatrix()')
    print Eval('OM.MMatrix().matrix')

if __name__ == "__main__":
    Testing_Vector_Attributes()