python-lottie  0.6.10+dev42a5126
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 __init__(self):
229  pass
230 
231  def to_dict(self):
232  return {
233  prop.lottie: prop.to_dict(self)
234  for prop in self._props
235  if prop.get(self) is not None
236  }
237 
238  @classmethod
239  def load(cls, lottiedict):
240  if "__pyclass" in lottiedict:
241  return CustomObject.load(lottiedict)
242  if not lottiedict:
243  return None
244  cls = cls._load_get_class(lottiedict)
245  obj = cls()
246  for prop in cls._props:
247  prop.load_into(lottiedict, obj)
248  return obj
249 
250  @classmethod
251  def _load_get_class(cls, lottiedict):
252  return cls
253 
254  def find(self, search, propname="name"):
255  """!
256  @param search The value of the property to search
257  @param propname The name of the property used to search
258  @brief Recursively searches for child objects with a matching property
259  """
260  if getattr(self, propname, None) == search:
261  return self
262  for prop in self._props:
263  v = prop.get(self)
264  if isinstance(v, LottieObject):
265  found = v.find(search, propname)
266  if found:
267  return found
268  elif isinstance(v, list) and v and isinstance(v[0], LottieObject):
269  for obj in v:
270  found = obj.find(search, propname)
271  if found:
272  return found
273  return None
274 
275  def find_all(self, type, predicate=None, include_self=True):
276  """!
277  Find all child objects that match a predicate
278  @param type Type (or tuple of types) of the objects to match
279  @param predicate Function that returns true on the objects to find
280  @param include_self Whether should counsider `self` for a potential match
281  """
282 
283  if isinstance(self, type) and include_self:
284  if not predicate or predicate(self):
285  yield self
286 
287  for prop in self._props:
288  v = prop.get(self)
289 
290  if isinstance(v, LottieObject):
291  for found in v.find_all(type, predicate, True):
292  yield found
293  elif isinstance(v, list) and v and isinstance(v[0], LottieObject):
294  for child in v:
295  for found in child.find_all(type, predicate, True):
296  yield found
297 
298  def clone(self):
299  obj = self.__class__()
300  for prop in self._props:
301  v = prop.get(self)
302  prop.set(obj, prop.clone_value(v))
303  return obj
304 
305  def clone_into(self, other):
306  for prop in self._props:
307  v = prop.get(self)
308  prop.set(other, prop.clone_value(v))
309 
310  def __str__(self):
311  return type(self).__name__
312 
313 
314 class Index:
315  """!
316  @brief Simple iterator to generate increasing integers
317  """
318  def __init__(self):
319  self._i = -1
320 
321  def __next__(self):
322  self._i += 1
323  return self._i
324 
325 
327  """!
328  Allows extending the Lottie shapes with custom Python classes
329  """
330  wrapped_lottie = LottieObject
331 
332  def __init__(self):
333  self.wrapped = self.wrapped_lottie()
334 
335  @classmethod
336  def load(cls, lottiedict):
337  ld = lottiedict.copy()
338  classname = ld.pop("__pyclass")
339  modn, clsn = classname.rsplit(".", 1)
340  subcls = getattr(importlib.import_module(modn), clsn)
341  obj = subcls()
342  for prop in subcls._props:
343  prop.load_into(lottiedict, obj)
344  obj.wrapped = subcls.wrapped_lottie.load(ld)
345  return obj
346 
347  def clone(self):
348  obj = self.__class__(**self.to_pyctor())
349  obj.wrapped = self.wrapped.clone()
350  return obj
351 
352  def to_dict(self):
353  dict = self.wrapped.to_dict()
354  dict["__pyclass"] = "{0.__module__}.{0.__name__}".format(self.__class__)
355  dict.update(LottieObject.to_dict(self))
356  return dict
357 
358  def _build_wrapped(self):
359  return self.wrapped_lottie()
360 
361  def refresh(self):
362  self.wrapped = self._build_wrapped()
363 
364 
366  DONT_RECURSE = object()
367 
368  def __call__(self, lottie_object):
369  self._process(lottie_object)
370 
371  def _process(self, lottie_object):
372  self.visit(lottie_object)
373  for p in lottie_object._props:
374  pval = p.get(lottie_object)
375  if self.visit_property(lottie_object, p, pval) is not self.DONT_RECURSE:
376  if isinstance(pval, LottieObject):
377  self._process(pval)
378  elif isinstance(pval, list) and pval and isinstance(pval[0], LottieObject):
379  for c in pval:
380  self._process(c)
381 
382  def visit(self, object):
383  pass
384 
385  def visit_property(self, object, property, value):
386  pass
lottie.objects.base.ObjectVisitor
Definition: base.py:365
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:352
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:319
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:254
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:275
lottie.objects.base.CustomObject.refresh
def refresh(self)
Definition: base.py:361
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:310
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:239
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:326
lottie.objects.base.CustomObject.clone
def clone(self)
Returns a copy of the object.
Definition: base.py:347
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:368
lottie.objects.base.ObjectVisitor.DONT_RECURSE
DONT_RECURSE
Definition: base.py:366
lottie.objects.base.ObjectVisitor._process
def _process(self, lottie_object)
Definition: base.py:371
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.LottieObject.__init__
def __init__(self)
Definition: base.py:228
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:385
lottie.objects.base.LottieObject.to_dict
def to_dict(self)
Serializes into a JSON object fit for the Lottie format.
Definition: base.py:231
lottie.objects.base.LottieObject.clone_into
def clone_into(self, other)
Definition: base.py:305
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:314
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:330
lottie.objects.base.CustomObject.load
def load(cls, lottiedict)
Loads from a JSON object.
Definition: base.py:336
lottie.objects.base.Index.__init__
def __init__(self)
Definition: base.py:318
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:358
lottie.objects.base.LottieObject._load_get_class
def _load_get_class(cls, lottiedict)
Definition: base.py:251
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:298
lottie.objects.base.ObjectVisitor.visit
def visit(self, object)
Definition: base.py:382
lottie.objects.base.CustomObject.wrapped
wrapped
Definition: base.py:333
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:321
lottie.objects.base.CustomObject.__init__
def __init__(self)
Definition: base.py:332
lottie.nvector.NVector
Definition: nvector.py:9
lottie.objects.base.LottieValueConverter.__name__
def __name__(self)
Definition: base.py:80