From d6cb013cc3900e9ffff914fc624d2692e33bda42 Mon Sep 17 00:00:00 2001 From: Darren Ranalli Date: Wed, 25 May 2005 23:05:29 +0000 Subject: [PATCH] added POD, ParamSet inheritance check, fixed ParamObj.__repr__ --- direct/src/showbase/PythonUtil.py | 134 +++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py index 3b6b33ec54..bc55ac8d6f 100644 --- a/direct/src/showbase/PythonUtil.py +++ b/direct/src/showbase/PythonUtil.py @@ -982,6 +982,7 @@ class ParamObj: # END PARAMSET SUBCLASS def __init__(self, *args, **kwArgs): + assert(issubclass(self.ParamSet, ParamObj.ParamSet)) # If you pass in a ParamSet obj, its values will be applied to this # object in the constructor. params = None @@ -1107,7 +1108,138 @@ class ParamObj: def __repr__(self): argStr = '' for param in self.ParamSet.getParams(): - argStr += '%s=%s,' % (param, getSetter(self, param, 'get')()) + argStr += '%s=%s,' % (param, + repr(getSetter(self, param, 'get')())) + return '%s(%s)' % (self.__class__.__name__, argStr) + +""" +POD (Plain Ol' Data) + +Like ParamObj/ParamSet, but without lock/unlock/getPriorValue and without +appliers. Similar to a C++ struct, but with auto-generated setters and +getters. + +Use POD when you want the generated getters and setters of ParamObj, but +efficiency is a concern and you don't need the bells and whistles provided +by ParamObj. + +POD.__init__ *MUST* be called. You should NOT define your own data getters +and setters. Data values may be read, set, and modified directly. You will +see no errors if you define your own getters/setters, but there is no +guarantee that they will be called--and they will certainly be bypassed by +POD internally. + +EXAMPLE CLASSES +=============== +Here is an example of a class heirarchy that uses POD to manage its data: + +class Enemy(POD): + DataSet = { + 'faction': 'navy', + } + +class Sailor(Enemy): + DataSet = { + 'build': HUSKY, + 'weapon': Cutlass(scale=.9), + } + +EXAMPLE USAGE +============= +s = Sailor(faction='undead', build=SKINNY) + +# make two copies of s +s2 = s.makeCopy() +s3 = Sailor(s) + +# example sets +s2.setWeapon(Musket()) +s3.build = TALL + +# example gets +faction2 = s2.getFaction() +faction3 = s3.faction +""" +class POD: + DataSet = { + # base class does not define any data items, but they would + # appear here as 'name': value, + } + def __init__(self, *args, **kwArgs): + self.__class__._compileDefaultDataSet() + if len(args) == 1 and len(kwArgs) == 0: + # extract our dataset from an existing POD instance + obj = args[0] + for name in self.getDataNames(): + setattr(self, name, getattr(obj, name)) + else: + assert len(args) == 0 + if __debug__: + for arg in kwArgs.keys(): + assert arg in self.getDataNames() + for name in self.getDataNames(): + if name in kwArgs: + setattr(self, name, kwArgs[name]) + else: + setattr(self, name, self.getDefaultValue(name)) + + def setDefaultValues(self): + # set all the default data values on ourself + for name in self.getDataNames(): + setattr(self, name, self.getDefaultValue(name)) + def makeCopy(self): + # returns a duplicate of this object + return self.__class__(self) + def applyTo(self, obj): + # Apply our entire set of data to another POD + for name in self.getDataNames(): + setattr(obj, name, getattr(self, name)) + def getValue(self, name): + return getattr(self, name) + + # CLASS METHODS + def getDataNames(cls): + # returns safely-mutable list of datum names + cls._compileDefaultDataSet() + return cls._DataSet.keys() + getDataNames = classmethod(getDataNames) + def getDefaultValue(cls, name): + cls._compileDefaultDataSet() + return cls._DataSet[name] + getDefaultValue = classmethod(getDefaultValue) + def _compileDefaultDataSet(cls): + if cls.__dict__.has_key('_DataSet'): + # we've already compiled the defaults for this class + return + # create setters & getters for this class + if cls.__dict__.has_key('DataSet'): + for name in cls.DataSet: + def defaultSetter(self, value, name=name): + setattr(self, name, value) + cls.__dict__[getSetterName(name)] = defaultSetter + def defaultGetter(self, name=name, + default=cls.DataSet[name]): + return getattr(self, name, default) + cls.__dict__[getSetterName(name, 'get')] = defaultGetter + # this dict will hold all of the aggregated default data values for + # this particular class, including values from its base classes + cls._DataSet = {} + bases = list(cls.__bases__) + # bring less-derived classes to the front + mostDerivedLast(bases) + for c in (bases + [cls]): + # make sure this base has its dict of data defaults + c._compileDefaultDataSet() + if c.__dict__.has_key('DataSet'): + # apply this class' default data values to our dict + cls._DataSet.update(c.DataSet) + _compileDefaultDataSet = classmethod(_compileDefaultDataSet) + # END CLASS METHODS + + def __repr__(self): + argStr = '' + for name in self.getDataNames(): + argStr += '%s=%s,' % (name, repr(getattr(self, name))) return '%s(%s)' % (self.__class__.__name__, argStr) def bound(value, bound1, bound2):