FreeCAD_assembly3/proxy.py

239 lines
7.4 KiB
Python

import past.builtins as pb
from collections import namedtuple
from asm3.utils import logger, objName
def propGet(self,obj):
return getattr(obj,self.Name)
def propGetValue(self,obj):
return getattr(getattr(obj,self.Name),'Value')
class PropertyInfo(object):
def __init__(self,host,name,tp,doc='', enum=None,
getter=propGet,group='Base',internal=False,duplicate=False):
self.Name = name
self.Type = tp
self.Group = group
self.Doc = doc
self.Enum = enum
self.get = getter.__get__(self,self.__class__)
self.Internal = internal
self.Key = host.addPropertyInfo(self,duplicate)
class ProxyType(type):
_typeID = '_ProxyType'
_typeEnum = 'ProxyType'
_typeGroup = 'Base'
_proxyName = '_proxy'
_registry = {}
Info = namedtuple('ProxyTypeInfo',
('Types','TypeMap','TypeNameMap','TypeNames','PropInfo'))
@classmethod
def getMetaName(mcs):
return mcs.__name__
@classmethod
def getInfo(mcs):
if not getattr(mcs,'_info',None):
mcs._info = mcs.Info([],{},{},[],{})
mcs._registry[mcs.getMetaName()] = mcs._info
return mcs._info
@classmethod
def reload(mcs):
info = mcs.getInfo()
mcs._info = None
for tp in info.Types:
tp._idx = -1
mcs.getInfo().Types.append(tp)
mcs.register(tp)
@classmethod
def getType(mcs,tp):
if isinstance(tp,pb.basestring):
return mcs.getInfo().TypeNameMap[tp]
if not isinstance(tp,int):
tp = mcs.getTypeID(tp)
return mcs.getInfo().TypeMap[tp]
@classmethod
def getTypeID(mcs,obj):
return getattr(obj,mcs._typeID,-1)
@classmethod
def setTypeID(mcs,obj,tp):
setattr(obj,mcs._typeID,tp)
@classmethod
def getTypeName(mcs,obj):
return getattr(obj,mcs._typeEnum,None)
@classmethod
def setTypeName(mcs,obj,tp):
setattr(obj,mcs._typeEnum,tp)
@classmethod
def getProxy(mcs,obj):
return getattr(obj.Proxy,mcs._proxyName,None)
@classmethod
def setProxy(mcs,obj):
cls = mcs.getType(mcs.getTypeName(obj))
proxy = mcs.getProxy(obj)
if type(proxy) is not cls:
logger.debug('attaching {}, {} -> {}'.format(
objName(obj),type(proxy).__name__,cls.__name__),frame=1)
if proxy:
mcs.detach(obj)
if mcs.getTypeID(obj) != cls._id:
mcs.setTypeID(obj,cls._id)
props = cls.getPropertyInfoList()
if props:
oprops = obj.PropertiesList
for key in props:
prop = mcs.getPropertyInfo(key)
if prop.Name not in oprops:
obj.addProperty(prop.Type,prop.Name,prop.Group,prop.Doc)
if prop.Enum:
setattr(obj,prop.Name,prop.Enum)
else:
obj.setPropertyStatus(prop.Name,'-Hidden')
setattr(obj.Proxy,mcs._proxyName,cls(obj))
obj.ViewObject.signalChangeIcon()
return obj
@classmethod
def detach(mcs,obj,detachAll=False):
proxy = mcs.getProxy(obj)
if proxy:
logger.debug('detaching {}<{}>'.format(objName(obj),
proxy.__class__.__name__))
for key in proxy.getPropertyInfoList():
prop = mcs.getPropertyInfo(key)
# obj.setPropertyStatus(prop.Name,'Hidden')
obj.removeProperty(prop.Name)
callback = getattr(proxy,'onDetach',None)
if callback:
callback(obj)
setattr(obj.Proxy,mcs._proxyName,None)
if detachAll:
obj.removeProperty(mcs._typeID)
obj.removeProperty(mcs._typeEnum)
@classmethod
def setDefaultTypeID(mcs,obj,name=None):
info = mcs.getInfo()
if not name:
name = info.TypeNames[0]
mcs.setTypeID(obj,info.TypeNameMap[name]._id)
@classmethod
def attach(mcs,obj,checkType=True):
info = mcs.getInfo()
if not info.TypeNames:
logger.error('"{}" has no registered types'.format(
mcs.getMetaName()))
return
if checkType:
if mcs._typeID not in obj.PropertiesList:
obj.addProperty("App::PropertyInteger",
mcs._typeID,mcs._typeGroup,'',0,False,True)
mcs.setDefaultTypeID(obj)
if mcs._typeEnum not in obj.PropertiesList:
logger.debug('type enum {}, {}'.format(mcs._typeEnum,
mcs._typeGroup))
obj.addProperty("App::PropertyEnumeration",
mcs._typeEnum,mcs._typeGroup,'',2)
mcs.setTypeName(obj,info.TypeNames)
idx = 0
try:
idx = mcs.getType(obj)._idx
except KeyError:
logger.warn('{} has unknown {} type {}'.format(
objName(obj),mcs.getMetaName(),mcs.getTypeID(obj)))
mcs.setTypeName(obj,idx)
return mcs.setProxy(obj)
@classmethod
def onChanged(mcs,obj,prop):
if prop == mcs._typeEnum:
if mcs.getProxy(obj):
return mcs.attach(obj,False)
elif prop == mcs._typeID:
if mcs.getProxy(obj):
cls = mcs.getType(mcs.getTypeID(obj))
if mcs.getTypeName(obj)!=cls.getName():
mcs.setTypeName(obj,cls._idx)
def __init__(cls, name, bases, attrs):
super(ProxyType,cls).__init__(name,bases,attrs)
cls._idx = -1
mcs = cls.__class__
mcs.getInfo().Types.append(cls)
mcs.register(cls)
@classmethod
def register(mcs,cls):
'''
Register a class to this meta class
To make the registration automatic at the class definition time, simply
set __metaclass__ of that class to ProxyType of its derived type.
You can also call this methode directly to register an unrelated class
'''
if cls._id < 0:
return
info = mcs.getInfo()
if cls._id in info.TypeMap:
raise RuntimeError('Duplicate {} type id {}'.format(
mcs.getMetaName(),cls._id))
info.TypeMap[cls._id] = cls
info.TypeNameMap[cls.getName()] = cls
info.TypeNames.append(cls.getName())
cls._idx = len(info.TypeNames)-1
logger.trace('register {} "{}":{},{}'.format(
mcs.getMetaName(),cls.getName(),cls._id,cls._idx))
@classmethod
def addPropertyInfo(mcs,info,duplicate):
props = mcs.getInfo().PropInfo
key = info.Name
i = 1
while key in props:
if not duplicate:
raise RuntimeError('Duplicate property "{}"'.format(info.Name))
key = key+str(i)
i = i+1
props[key] = info
return key
@classmethod
def getPropertyInfo(mcs,key):
return mcs.getInfo().PropInfo[key]
def getPropertyValues(cls,obj):
props = []
mcs = cls.__class__
for key in cls.getPropertyInfoList():
prop = mcs.getPropertyInfo(key)
if not prop.Internal:
props.append(prop.get(obj))
return props
def getPropertyInfoList(cls):
return []
def getName(cls):
return cls.__name__