161 lines
4.3 KiB
Python
161 lines
4.3 KiB
Python
'''
|
|
Cover Behavior
|
|
==============
|
|
|
|
The :class:`~kivy.uix.behaviors.cover.CoverBehavior`
|
|
`mixin <https://en.wikipedia.org/wiki/Mixin>`_ is intended for rendering
|
|
textures to full widget size keeping the aspect ratio of the original texture.
|
|
|
|
Use cases are i.e. rendering full size background images or video content in
|
|
a dynamic layout.
|
|
|
|
For an overview of behaviors, please refer to the :mod:`~kivy.uix.behaviors`
|
|
documentation.
|
|
|
|
Example
|
|
-------
|
|
|
|
The following examples add cover behavior to an image:
|
|
|
|
In python:
|
|
|
|
.. code-block:: python
|
|
|
|
from kivy.app import App
|
|
from kivy.uix.behaviors import CoverBehavior
|
|
from kivy.uix.image import Image
|
|
|
|
|
|
class CoverImage(CoverBehavior, Image):
|
|
|
|
def __init__(self, **kwargs):
|
|
super(CoverImage, self).__init__(**kwargs)
|
|
texture = self._coreimage.texture
|
|
self.reference_size = texture.size
|
|
self.texture = texture
|
|
|
|
|
|
class MainApp(App):
|
|
|
|
def build(self):
|
|
return CoverImage(source='image.jpg')
|
|
|
|
MainApp().run()
|
|
|
|
In Kivy Language:
|
|
|
|
.. code-block:: kv
|
|
|
|
CoverImage:
|
|
source: 'image.png'
|
|
|
|
<CoverImage@CoverBehavior+Image>:
|
|
reference_size: self.texture_size
|
|
|
|
See :class:`~kivy.uix.behaviors.cover.CoverBehavior` for details.
|
|
'''
|
|
|
|
__all__ = ('CoverBehavior', )
|
|
|
|
from decimal import Decimal
|
|
from kivy.lang import Builder
|
|
from kivy.properties import ListProperty
|
|
|
|
|
|
Builder.load_string("""
|
|
<-CoverBehavior>:
|
|
canvas.before:
|
|
StencilPush
|
|
Rectangle:
|
|
pos: self.pos
|
|
size: self.size
|
|
StencilUse
|
|
canvas:
|
|
Rectangle:
|
|
texture: self.texture
|
|
size: self.cover_size
|
|
pos: self.cover_pos
|
|
canvas.after:
|
|
StencilUnUse
|
|
Rectangle:
|
|
pos: self.pos
|
|
size: self.size
|
|
StencilPop
|
|
""")
|
|
|
|
|
|
class CoverBehavior(object):
|
|
'''The CoverBehavior `mixin <https://en.wikipedia.org/wiki/Mixin>`_
|
|
provides rendering a texture covering full widget size keeping aspect ratio
|
|
of the original texture.
|
|
|
|
.. versionadded:: 1.10.0
|
|
'''
|
|
|
|
reference_size = ListProperty([])
|
|
'''Reference size used for aspect ratio approximation calculation.
|
|
|
|
:attr:`reference_size` is a :class:`~kivy.properties.ListProperty` and
|
|
defaults to `[]`.
|
|
'''
|
|
|
|
cover_size = ListProperty([0, 0])
|
|
'''Size of the aspect ratio aware texture. Gets calculated in
|
|
``CoverBehavior.calculate_cover``.
|
|
|
|
:attr:`cover_size` is a :class:`~kivy.properties.ListProperty` and
|
|
defaults to `[0, 0]`.
|
|
'''
|
|
|
|
cover_pos = ListProperty([0, 0])
|
|
'''Position of the aspect ratio aware texture. Gets calculated in
|
|
``CoverBehavior.calculate_cover``.
|
|
|
|
:attr:`cover_pos` is a :class:`~kivy.properties.ListProperty` and
|
|
defaults to `[0, 0]`.
|
|
'''
|
|
|
|
def __init__(self, **kwargs):
|
|
super(CoverBehavior, self).__init__(**kwargs)
|
|
# bind covering
|
|
self.bind(
|
|
size=self.calculate_cover,
|
|
pos=self.calculate_cover
|
|
)
|
|
|
|
def _aspect_ratio_approximate(self, size):
|
|
# return a decimal approximation of an aspect ratio.
|
|
return Decimal('%.2f' % (float(size[0]) / size[1]))
|
|
|
|
def _scale_size(self, size, sizer):
|
|
# return scaled size based on sizer, where sizer (n, None) scales x
|
|
# to n and (None, n) scales y to n
|
|
size_new = list(sizer)
|
|
i = size_new.index(None)
|
|
j = i * -1 + 1
|
|
size_new[i] = (size_new[j] * size[i]) / size[j]
|
|
return tuple(size_new)
|
|
|
|
def calculate_cover(self, *args):
|
|
# return if no reference size yet
|
|
if not self.reference_size:
|
|
return
|
|
size = self.size
|
|
origin_appr = self._aspect_ratio_approximate(self.reference_size)
|
|
crop_appr = self._aspect_ratio_approximate(size)
|
|
# same aspect ratio
|
|
if origin_appr == crop_appr:
|
|
crop_size = self.size
|
|
offset = (0, 0)
|
|
# scale x
|
|
elif origin_appr < crop_appr:
|
|
crop_size = self._scale_size(self.reference_size, (size[0], None))
|
|
offset = (0, ((crop_size[1] - size[1]) / 2) * -1)
|
|
# scale y
|
|
else:
|
|
crop_size = self._scale_size(self.reference_size, (None, size[1]))
|
|
offset = (((crop_size[0] - size[0]) / 2) * -1, 0)
|
|
# set background size and position
|
|
self.cover_size = crop_size
|
|
self.cover_pos = offset
|