簡體   English   中英

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

[英]OpenMaya API (python): print more useful info from MObjects? E.g. MFloatVector(x, y, z)

使用常規的python對象,很容易看到它們的詳細信息。 例如

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 <<運算符在C ++中獲得所需的信息。 例如,MFloatVector的<<表示:

使用的格式為[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來提供更多有用的打印詳細信息,那就是我會接受的答案。

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)

=>

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

好吧, 可以將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()以確定其是否可迭代,等等。這樣,您可以得到很好的傳播,但仍然進行了太多的工作而沒有太多收獲。

這是定義“ Repr(self)”的代碼。 對於大多數對象返回“ repr(self)”的全局函數。 但是對於返回以“ <”開頭的表示形式的對象,結果是一個成員值列表,這些成員值(1)不是內部的(不要以'__'開頭),而(2)不是方法。

例如,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()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM