Merge pull request #3900 from rnixx/cover_behavior

Add kivy.uix.behaviors.CoverBehavior
This commit is contained in:
Akshay Arora 2017-01-27 17:33:13 +05:30 committed by GitHub
commit c6965414f2
6 changed files with 204 additions and 1 deletions

View File

@ -0,0 +1,25 @@
import kivy
from kivy.app import App
from kivy.uix.behaviors import CoverBehavior
from kivy.uix.image import Image
class CoverImage(CoverBehavior, Image):
"""Image using cover behavior.
"""
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')
if __name__ == '__main__':
MainApp().run()

View File

@ -0,0 +1,31 @@
import kivy
from kivy.app import App
from kivy.uix.behaviors import CoverBehavior
from kivy.uix.video import Video
class CoverVideo(CoverBehavior, Video):
"""Video using cover behavior.
"""
def _on_video_frame(self, *largs):
video = self._video
if not video:
return
texture = video.texture
self.reference_size = texture.size
self.calculate_cover()
self.duration = video.duration
self.position = video.position
self.texture = texture
self.canvas.ask_update()
class MainApp(App):
def build(self):
return CoverVideo(source='video.mpg', play=True)
if __name__ == '__main__':
MainApp().run()

BIN
examples/cover/image.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
examples/cover/video.mpg Normal file

Binary file not shown.

View File

@ -80,7 +80,7 @@ offers:
__all__ = ('ButtonBehavior', 'ToggleButtonBehavior', 'DragBehavior',
'FocusBehavior', 'CompoundSelectionBehavior',
'CodeNavigationBehavior', 'EmacsBehavior')
'CodeNavigationBehavior', 'EmacsBehavior', 'CoverBehavior')
from kivy.uix.behaviors.button import ButtonBehavior
from kivy.uix.behaviors.togglebutton import ToggleButtonBehavior
@ -89,3 +89,4 @@ from kivy.uix.behaviors.focus import FocusBehavior
from kivy.uix.behaviors.compoundselection import CompoundSelectionBehavior
from kivy.uix.behaviors.codenavigation import CodeNavigationBehavior
from kivy.uix.behaviors.emacs import EmacsBehavior
from kivy.uix.behaviors.cover import CoverBehavior

146
kivy/uix/behaviors/cover.py Normal file
View File

@ -0,0 +1,146 @@
'''
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 example adds cover behavior to an image::
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()
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