python-lottie  0.6.10+dev4166086
A framework to work with lottie files and telegram animated stickers (tgs)
base.py
Go to the documentation of this file.
1 import enum
2 import inspect
3 import importlib
4 from ..nvector import NVector
5 from ..utils.color import Color
6 
7 
8 class LottieBase:
9  """!
10  Base class for Lottie JSON objects bindings
11  """
12  def to_dict(self):
13  """!
14  Serializes into a JSON object fit for the Lottie format
15  """
16  raise NotImplementedError
17 
18  @classmethod
19  def load(cls, lottiedict):
20  """!
21  Loads from a JSON object
22  @returns An instance of the class
23  """
24  raise NotImplementedError
25 
26  def clone(self):
27  """!
28  Returns a copy of the object
29  """
30  raise NotImplementedError
31 
32 
33 class EnumMeta(enum.EnumMeta):
34  """!
35  Hack to counter-hack the hack in enum meta
36  """
37  def __new__(cls, name, bases, classdict):
38  classdict["__reduce_ex__"] = lambda *a, **kw: None # pragma: no cover
39  return super().__new__(cls, name, bases, classdict)
40 
41 
42 class LottieEnum(LottieBase, enum.Enum, metaclass=EnumMeta):
43  """!
44  Base class for enum-like types in the Lottie JSON structure
45  """
46  def to_dict(self):
47  return self.value
48 
49  @classmethod
50  def load(cls, lottieint):
51  return cls(lottieint)
52 
53  def clone(self):
54  return self
55 
56 
57 class PseudoList:
58  """!
59  List tag for some weird values in the Lottie JSON
60  """
61  pass
62 
63 
64 class LottieValueConverter:
65  """!
66  Factory for property types that require special conversions
67  """
68  def __init__(self, py, lottie, name=None):
69  self.py = py
70  self.lottie = lottie
71  self.name = name or "%s but displayed as %s" % (self.py.__name__, self.lottie.__name__)
72 
73  def py_to_lottie(self, val):
74  return self.lottie(val)
75 
76  def lottie_to_py(self, val):
77  return self.py(val)
78 
79  @property
80  def __name__(self):
81  return self.name
82 
83 
84 ## For values in Lottie that are bools but ints in the JSON
85 PseudoBool = LottieValueConverter(bool, int, "0-1 int")
86 
87 
88 class LottieProp:
89  """!
90  Lottie <-> Python property mapper
91  """
92  def __init__(self, name, lottie, type=float, list=False, cond=None):
93  ## Name of the Python property
94  self.name = name
95  ## Name of the Lottie JSON property
96  self.lottie = lottie
97  ## Type of the property
98  ## @see LottieValueConverter, PseudoBool
99  self.type = type
100  ## Whether the property is a list of self.type
101  ## @see PseudoList
102  self.list = list
103  ## Condition on when the property is loaded from the Lottie JSON
104  self.cond = cond
105 
106  def get(self, obj):
107  """!
108  Returns the value of the property from a Python object
109  """
110  return getattr(obj, self.name)
111 
112  def set(self, obj, value):
113  """!
114  Sets the value of the property from a Python object
115  """
116  if isinstance(getattr(obj.__class__, self.name, None), property):
117  return
118  return setattr(obj, self.name, value)
119 
120  def load_from_parent(self, lottiedict):
121  """!
122  Returns the value for this property from a JSON dict representing the parent object
123  @returns The loaded value or @c None if the property is not in @p lottiedict
124  """
125  if self.lottie in lottiedict:
126  return self.load(lottiedict[self.lottie])
127  return None
128 
129  def load_into(self, lottiedict, obj):
130  """!
131  Loads from a Lottie dict into an object
132  """
133  if self.cond and not self.cond(lottiedict):
134  return
135  self.set(obj, self.load_from_parent(lottiedict))
136 
137  def load(self, lottieval):
138  """!
139  Loads the property from a JSON value
140  @returns the Python equivalent of the JSON value
141  """
142  if self.list is PseudoList and isinstance(lottieval, list):
143  return self._load_scalar(lottieval[0])
144  #return [
145  #self._load_scalar(it)
146  #for it in lottieval
147  #]
148  elif self.list is True:
149  return list(filter(lambda x: x is not None, (
150  self._load_scalar(it)
151  for it in lottieval
152  )))
153  return self._load_scalar(lottieval)
154 
155  def _load_scalar(self, lottieval):
156  if lottieval is None:
157  return None
158  if inspect.isclass(self.type) and issubclass(self.type, LottieBase):
159  return self.type.load(lottieval)
160  elif isinstance(self.type, type) and isinstance(lottieval, self.type):
161  return lottieval
162  elif isinstance(self.type, LottieValueConverter):
163  return self.type.lottie_to_py(lottieval)
164  elif self.type is NVector:
165  return NVector(*lottieval)
166  elif self.type is Color:
167  return Color(*lottieval)
168  if isinstance(lottieval, list) and lottieval:
169  lottieval = lottieval[0]
170  return self.type(lottieval)
171 
172  def to_dict(self, obj):
173  """!
174  Converts the value of the property as from @p obj into a JSON value
175  @param obj LottieObject with this property
176  """
177  val = self._basic_to_dict(self.get(obj))
178  if self.list is PseudoList:
179  if not isinstance(obj, list):
180  return [val]
181  elif isinstance(self.type, LottieValueConverter):
182  val = self._basic_to_dict(self.type.py_to_lottie(val))
183  return val
184 
185  def _basic_to_dict(self, v):
186  if isinstance(v, LottieBase):
187  return v.to_dict()
188  elif isinstance(v, NVector):
189  return list(map(self._basic_to_dict, v.components))
190  elif isinstance(v, list):
191  return list(map(self._basic_to_dict, v))
192  elif isinstance(v, (int, str, bool)):
193  return v
194  elif isinstance(v, float):
195  if v % 1 == 0:
196  return int(v)
197  return v #round(v, 3)
198  else:
199  raise Exception("Unknown value %r" % v)
200 
201  def __repr__(self):
202  return "<LottieProp %s:%s>" % (self.name, self.lottie)
203 
204  def clone_value(self, value):
205  if isinstance(value, list):
206  return [self.clone_value(v) for v in value]
207  if isinstance(value, (LottieBase, NVector)):
208  return value.clone()
209  if isinstance(value, (int, float, bool, str)) or value is None:
210  return value
211  raise Exception("Could not convert %r" % value)
212 
213 
214 class LottieObjectMeta(type):
215  def __new__(cls, name, bases, attr):
216  props = []
217  for base in bases:
218  if type(base) == cls:
219  props += base._props
220  attr["_props"] = props + attr.get("_props", [])
221  return super().__new__(cls, name, bases, attr)
222 
223 
224 class LottieObject(LottieBase, metaclass=LottieObjectMeta):
225  """!
226  @brief Base class for mapping Python classes into Lottie JSON objects
227  """
228  def to_dict(self):
229  return {
230  prop.lottie: prop.to_dict(self)
231  for prop in self._props
232  if prop.get(self) is not None
233  }
234 
235  @classmethod
236  def load(cls, lottiedict):
237  if "__pyclass" in lottiedict:
238  return CustomObject.load(lottiedict)
239  if not lottiedict:
240  return None
241  cls = cls._load_get_class(lottiedict)
242  obj = cls()
243  for prop in cls._props:
244  prop.load_into(lottiedict, obj)
245  return obj
246 
247  @classmethod
248  def _load_get_class(cls, lottiedict):
249  return cls
250 
251  def find(self, search, propname="name"):
252  """!
253  @param search The value of the property to search
254  @param propname The name of the property used to search
255  @brief Recursively searches for child objects with a matching property
256  """
257  if getattr(self, propname, None) == search:
258  return self
259  for prop in self._props:
260  v = prop.get(self)
261  if isinstance(v, LottieObject):
262  found = v.find(search, propname)
263  if found:
264  return found
265  elif isinstance(v, list) and v and isinstance(v[0], LottieObject):
266  for obj in v:
267  found = obj.find(search, propname)
268  if found:
269  return found
270  return None
271 
272  def find_all(self, type, predicate=None, include_self=True):
273  """!
274  Find all child objects that match a predicate
275  @param type Type (or tuple of types) of the objects to match
276  @param predicate Function that returns true on the objects to find
277  @param include_self Whether should counsider `self` for a potential match
278  """
279 
280  if isinstance(self, type) and include_self:
281  if not predicate or predicate(self):
282  yield self
283 
284  for prop in self._props:
285  v = prop.get(self)
286 
287  if isinstance(v, LottieObject):
288  for found in v.find_all(type, predicate, True):
289  yield found
290  elif isinstance(v, list) and v and isinstance(v[0], LottieObject):
291  for child in v:
292  for found in child.find_all(type, predicate, True):
293  yield found
294 
295  def clone(self):
296  obj = self.__class__()
297  for prop in self._props:
298  v = prop.get(self)
299  prop.set(obj, prop.clone_value(v))
300  return obj
301 
302  def __str__(self):
303  return type(self).__name__
304 
305 
306 class Index:
307  """!
308  @brief Simple iterator to generate increasing integers
309  """
310  def __init__(self):
311  self._i = -1
312 
313  def __next__(self):
314  self._i += 1
315  return self._i
316 
317 
319  """!
320  Allows extending the Lottie shapes with custom Python classes
321  """
322  wrapped_lottie = LottieObject
323 
324  def __init__(self):
325  self.wrapped = self.wrapped_lottie()
326 
327  @classmethod
328  def load(cls, lottiedict):
329  ld = lottiedict.copy()
330  classname = ld.pop("__pyclass")
331  modn, clsn = classname.rsplit(".", 1)
332  subcls = getattr(importlib.import_module(modn), clsn)
333  obj = subcls()
334  for prop in subcls._props:
335  prop.load_into(lottiedict, obj)
336  obj.wrapped = subcls.wrapped_lottie.load(ld)
337  return obj
338 
339  def clone(self):
340  obj = self.__class__(**self.to_pyctor())
341  obj.wrapped = self.wrapped.clone()
342  return obj
343 
344  def to_dict(self):
345  dict = self.wrapped.to_dict()
346  dict["__pyclass"] = "{0.__module__}.{0.__name__}".format(self.__class__)
347  dict.update(LottieObject.to_dict(self))
348  return dict
349 
350  def _build_wrapped(self):
351  return self.wrapped_lottie()
352 
353  def refresh(self):
354  self.wrapped = self._build_wrapped()
355 
356 
358  DONT_RECURSE = object()
359 
360  def __call__(self, lottie_object):
361  self._process(lottie_object)
362 
363  def _process(self, lottie_object):
364  self.visit(lottie_object)
365  for p in lottie_object._props:
366  pval = p.get(lottie_object)
367  if self.visit_property(lottie_object, p, pval) is not self.DONT_RECURSE:
368  if isinstance(pval, LottieObject):
369  self._process(pval)
370  elif isinstance(pval, list) and pval and isinstance(pval[0], LottieObject):
371  for c in pval:
372  self._process(c)
373 
374  def visit(self, object):
375  pass
376 
377  def visit_property(self, object, property, value):
378  pass
lottie.objects.base.ObjectVisitor
Definition: base.py:357
lottie.objects.base.LottieProp.load
def load(self, lottieval)
Loads the property from a JSON value.
Definition: base.py:137
lottie.objects.base.CustomObject.to_dict
def to_dict(self)
Serializes into a JSON object fit for the Lottie format.
Definition: base.py:344
lottie.utils.color.Color
Definition: color.py:368
lottie.objects.base.LottieBase.to_dict
def to_dict(self)
Serializes into a JSON object fit for the Lottie format.
Definition: base.py:12
lottie.objects.base.Index._i
_i
Definition: base.py:311
lottie.objects.base.EnumMeta
Hack to counter-hack the hack in enum meta.
Definition: base.py:33
lottie.objects.base.LottieEnum.to_dict
def to_dict(self)
Serializes into a JSON object fit for the Lottie format.
Definition: base.py:46
lottie.objects.base.LottieProp.clone_value
def clone_value(self, value)
Definition: base.py:204
lottie.objects.base.LottieProp.__init__
def __init__(self, name, lottie, type=float, list=False, cond=None)
Definition: base.py:92
lottie.objects.base.LottieObject.find
def find(self, search, propname="name")
Recursively searches for child objects with a matching property.
Definition: base.py:251
lottie.objects.base.LottieObjectMeta.__new__
def __new__(cls, name, bases, attr)
Definition: base.py:215
lottie.objects.base.LottieObject.find_all
def find_all(self, type, predicate=None, include_self=True)
Find all child objects that match a predicate.
Definition: base.py:272
lottie.objects.base.CustomObject.refresh
def refresh(self)
Definition: base.py:353
lottie.objects.base.LottieEnum.clone
def clone(self)
Returns a copy of the object.
Definition: base.py:53
lottie.objects.base.LottieBase.clone
def clone(self)
Returns a copy of the object.
Definition: base.py:26
lottie.objects.base.LottieObject.__str__
def __str__(self)
Definition: base.py:302
lottie.objects.base.EnumMeta.__new__
def __new__(cls, name, bases, classdict)
Definition: base.py:37
lottie.objects.base.LottieObject
Base class for mapping Python classes into Lottie JSON objects.
Definition: base.py:224
lottie.objects.base.LottieObject.load
def load(cls, lottiedict)
Loads from a JSON object.
Definition: base.py:236
lottie.objects.base.PseudoList
List tag for some weird values in the Lottie JSON.
Definition: base.py:57
lottie.objects.base.LottieProp.load_into
def load_into(self, lottiedict, obj)
Loads from a Lottie dict into an object.
Definition: base.py:129
lottie.objects.base.LottieValueConverter.py
py
Definition: base.py:69
lottie.objects.base.LottieEnum.load
def load(cls, lottieint)
Loads from a JSON object.
Definition: base.py:50
lottie.objects.base.LottieProp
Lottie <-> Python property mapper.
Definition: base.py:88
lottie.objects.base.CustomObject
Allows extending the Lottie shapes with custom Python classes.
Definition: base.py:318
lottie.objects.base.CustomObject.clone
def clone(self)
Returns a copy of the object.
Definition: base.py:339
lottie.objects.base.LottieProp._load_scalar
def _load_scalar(self, lottieval)
Definition: base.py:155
lottie.objects.base.LottieBase.load
def load(cls, lottiedict)
Loads from a JSON object.
Definition: base.py:19
lottie.objects.base.LottieValueConverter.name
name
Definition: base.py:71
lottie.objects.base.ObjectVisitor.__call__
def __call__(self, lottie_object)
Definition: base.py:360
lottie.objects.base.ObjectVisitor.DONT_RECURSE
DONT_RECURSE
Definition: base.py:358
lottie.objects.base.ObjectVisitor._process
def _process(self, lottie_object)
Definition: base.py:363
lottie.objects.base.LottieValueConverter.lottie
lottie
Definition: base.py:70
lottie.objects.base.LottieValueConverter.__init__
def __init__(self, py, lottie, name=None)
Definition: base.py:68
lottie.objects.base.LottieProp.__repr__
def __repr__(self)
Definition: base.py:201
lottie.objects.base.LottieProp._basic_to_dict
def _basic_to_dict(self, v)
Definition: base.py:185
lottie.objects.base.LottieProp.to_dict
def to_dict(self, obj)
Converts the value of the property as from obj into a JSON value.
Definition: base.py:172
lottie.objects.base.LottieProp.type
type
Type of the property.
Definition: base.py:99
lottie.objects.base.LottieEnum
Base class for enum-like types in the Lottie JSON structure.
Definition: base.py:42
lottie.objects.base.LottieObjectMeta
Definition: base.py:214
lottie.objects.base.LottieProp.lottie
lottie
Name of the Lottie JSON property.
Definition: base.py:96
lottie.objects.base.LottieProp.list
list
Whether the property is a list of self.type.
Definition: base.py:102
lottie.objects.base.ObjectVisitor.visit_property
def visit_property(self, object, property, value)
Definition: base.py:377
lottie.objects.base.LottieObject.to_dict
def to_dict(self)
Serializes into a JSON object fit for the Lottie format.
Definition: base.py:228
lottie.objects.base.LottieProp.name
name
Name of the Python property.
Definition: base.py:94
lottie.objects.base.Index
Simple iterator to generate increasing integers.
Definition: base.py:306
lottie.objects.base.LottieProp.get
def get(self, obj)
Returns the value of the property from a Python object.
Definition: base.py:106
lottie.objects.base.LottieProp.load_from_parent
def load_from_parent(self, lottiedict)
Returns the value for this property from a JSON dict representing the parent object.
Definition: base.py:120
lottie.objects.base.CustomObject.wrapped_lottie
wrapped_lottie
Definition: base.py:322
lottie.objects.base.CustomObject.load
def load(cls, lottiedict)
Loads from a JSON object.
Definition: base.py:328
lottie.objects.base.Index.__init__
def __init__(self)
Definition: base.py:310
lottie.objects.base.LottieValueConverter
Factory for property types that require special conversions.
Definition: base.py:64
lottie.objects.base.CustomObject._build_wrapped
def _build_wrapped(self)
Definition: base.py:350
lottie.objects.base.LottieObject._load_get_class
def _load_get_class(cls, lottiedict)
Definition: base.py:248
lottie.objects.base.LottieProp.set
def set(self, obj, value)
Sets the value of the property from a Python object.
Definition: base.py:112
lottie.objects.base.LottieBase
Base class for Lottie JSON objects bindings.
Definition: base.py:8
lottie.objects.base.LottieValueConverter.lottie_to_py
def lottie_to_py(self, val)
Definition: base.py:76
lottie.objects.base.LottieProp.cond
cond
Condition on when the property is loaded from the Lottie JSON.
Definition: base.py:104
lottie.objects.base.LottieObject.clone
def clone(self)
Returns a copy of the object.
Definition: base.py:295
lottie.objects.base.ObjectVisitor.visit
def visit(self, object)
Definition: base.py:374
lottie.objects.base.CustomObject.wrapped
wrapped
Definition: base.py:325
lottie.objects.base.LottieValueConverter.py_to_lottie
def py_to_lottie(self, val)
Definition: base.py:73
lottie.objects.base.Index.__next__
def __next__(self)
Definition: base.py:313
lottie.objects.base.CustomObject.__init__
def __init__(self)
Definition: base.py:324
lottie.nvector.NVector
Definition: nvector.py:9
lottie.objects.base.LottieValueConverter.__name__
def __name__(self)
Definition: base.py:80