Ajout du GUI
This commit is contained in:
434
kivy/uix/image.py
Normal file
434
kivy/uix/image.py
Normal file
@@ -0,0 +1,434 @@
|
||||
'''
|
||||
Image
|
||||
=====
|
||||
|
||||
The :class:`Image` widget is used to display an image::
|
||||
|
||||
Example in python::
|
||||
|
||||
wimg = Image(source='mylogo.png')
|
||||
|
||||
Kv Example::
|
||||
|
||||
Image:
|
||||
source: 'mylogo.png'
|
||||
size: self.texture_size
|
||||
|
||||
|
||||
Asynchronous Loading
|
||||
--------------------
|
||||
|
||||
To load an image asynchronously (for example from an external webserver), use
|
||||
the :class:`AsyncImage` subclass::
|
||||
|
||||
aimg = AsyncImage(source='http://mywebsite.com/logo.png')
|
||||
|
||||
This can be useful as it prevents your application from waiting until the image
|
||||
is loaded. If you want to display large images or retrieve them from URL's,
|
||||
using :class:`AsyncImage` will allow these resources to be retrieved on a
|
||||
background thread without blocking your application.
|
||||
|
||||
Alignment
|
||||
---------
|
||||
|
||||
By default, the image is centered and fits inside the widget bounding box.
|
||||
If you don't want that, you can set `allow_stretch` to True and `keep_ratio`
|
||||
to False.
|
||||
|
||||
You can also inherit from Image and create your own style. For example, if you
|
||||
want your image to be greater than the size of your widget, you could do::
|
||||
|
||||
class FullImage(Image):
|
||||
pass
|
||||
|
||||
And in your kivy language file::
|
||||
|
||||
<-FullImage>:
|
||||
canvas:
|
||||
Color:
|
||||
rgb: (1, 1, 1)
|
||||
Rectangle:
|
||||
texture: self.texture
|
||||
size: self.width + 20, self.height + 20
|
||||
pos: self.x - 10, self.y - 10
|
||||
|
||||
'''
|
||||
__all__ = ('Image', 'AsyncImage')
|
||||
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.core.image import Image as CoreImage
|
||||
from kivy.resources import resource_find
|
||||
from kivy.properties import StringProperty, ObjectProperty, ListProperty, \
|
||||
AliasProperty, BooleanProperty, NumericProperty, ColorProperty
|
||||
from kivy.logger import Logger
|
||||
|
||||
# delayed imports
|
||||
Loader = None
|
||||
|
||||
|
||||
class Image(Widget):
|
||||
'''Image class, see module documentation for more information.
|
||||
'''
|
||||
|
||||
source = StringProperty(None)
|
||||
'''Filename / source of your image.
|
||||
|
||||
:attr:`source` is a :class:`~kivy.properties.StringProperty` and
|
||||
defaults to None.
|
||||
'''
|
||||
|
||||
texture = ObjectProperty(None, allownone=True)
|
||||
'''Texture object of the image. The texture represents the original, loaded
|
||||
image texture. It is stretched and positioned during rendering according to
|
||||
the :attr:`allow_stretch` and :attr:`keep_ratio` properties.
|
||||
|
||||
Depending of the texture creation, the value will be a
|
||||
:class:`~kivy.graphics.texture.Texture` or a
|
||||
:class:`~kivy.graphics.texture.TextureRegion` object.
|
||||
|
||||
:attr:`texture` is an :class:`~kivy.properties.ObjectProperty` and defaults
|
||||
to None.
|
||||
'''
|
||||
|
||||
texture_size = ListProperty([0, 0])
|
||||
'''Texture size of the image. This represents the original, loaded image
|
||||
texture size.
|
||||
|
||||
.. warning::
|
||||
|
||||
The texture size is set after the texture property. So if you listen to
|
||||
the change on :attr:`texture`, the property texture_size will not be
|
||||
up-to-date. Use self.texture.size instead.
|
||||
'''
|
||||
|
||||
def get_image_ratio(self):
|
||||
if self.texture:
|
||||
return self.texture.width / float(self.texture.height)
|
||||
return 1.
|
||||
|
||||
mipmap = BooleanProperty(False)
|
||||
'''Indicate if you want OpenGL mipmapping to be applied to the texture.
|
||||
Read :ref:`mipmap` for more information.
|
||||
|
||||
.. versionadded:: 1.0.7
|
||||
|
||||
:attr:`mipmap` is a :class:`~kivy.properties.BooleanProperty` and defaults
|
||||
to False.
|
||||
'''
|
||||
|
||||
image_ratio = AliasProperty(get_image_ratio, bind=('texture',), cache=True)
|
||||
'''Ratio of the image (width / float(height).
|
||||
|
||||
:attr:`image_ratio` is an :class:`~kivy.properties.AliasProperty` and is
|
||||
read-only.
|
||||
'''
|
||||
|
||||
color = ColorProperty([1, 1, 1, 1])
|
||||
'''Image color, in the format (r, g, b, a). This attribute can be used to
|
||||
'tint' an image. Be careful: if the source image is not gray/white, the
|
||||
color will not really work as expected.
|
||||
|
||||
.. versionadded:: 1.0.6
|
||||
|
||||
:attr:`color` is a :class:`~kivy.properties.ColorProperty` and defaults to
|
||||
[1, 1, 1, 1].
|
||||
|
||||
.. versionchanged:: 2.0.0
|
||||
Changed from :class:`~kivy.properties.ListProperty` to
|
||||
:class:`~kivy.properties.ColorProperty`.
|
||||
'''
|
||||
|
||||
allow_stretch = BooleanProperty(False)
|
||||
'''If True, the normalized image size will be maximized to fit in the image
|
||||
box. Otherwise, if the box is too tall, the image will not be
|
||||
stretched more than 1:1 pixels.
|
||||
|
||||
.. versionadded:: 1.0.7
|
||||
|
||||
:attr:`allow_stretch` is a :class:`~kivy.properties.BooleanProperty` and
|
||||
defaults to False.
|
||||
'''
|
||||
|
||||
keep_ratio = BooleanProperty(True)
|
||||
'''If False along with allow_stretch being True, the normalized image
|
||||
size will be maximized to fit in the image box and ignores the aspect
|
||||
ratio of the image.
|
||||
Otherwise, if the box is too tall, the image will not be stretched more
|
||||
than 1:1 pixels.
|
||||
|
||||
.. versionadded:: 1.0.8
|
||||
|
||||
:attr:`keep_ratio` is a :class:`~kivy.properties.BooleanProperty` and
|
||||
defaults to True.
|
||||
'''
|
||||
|
||||
keep_data = BooleanProperty(False)
|
||||
'''If True, the underlying _coreimage will store the raw image data.
|
||||
This is useful when performing pixel based collision detection.
|
||||
|
||||
.. versionadded:: 1.3.0
|
||||
|
||||
:attr:`keep_data` is a :class:`~kivy.properties.BooleanProperty` and
|
||||
defaults to False.
|
||||
'''
|
||||
|
||||
anim_delay = NumericProperty(.25)
|
||||
'''Delay the animation if the image is sequenced (like an animated gif).
|
||||
If anim_delay is set to -1, the animation will be stopped.
|
||||
|
||||
.. versionadded:: 1.0.8
|
||||
|
||||
:attr:`anim_delay` is a :class:`~kivy.properties.NumericProperty` and
|
||||
defaults to 0.25 (4 FPS).
|
||||
'''
|
||||
|
||||
anim_loop = NumericProperty(0)
|
||||
'''Number of loops to play then stop animating. 0 means keep animating.
|
||||
|
||||
.. versionadded:: 1.9.0
|
||||
|
||||
:attr:`anim_loop` is a :class:`~kivy.properties.NumericProperty` and
|
||||
defaults to 0.
|
||||
'''
|
||||
|
||||
nocache = BooleanProperty(False)
|
||||
'''If this property is set True, the image will not be added to the
|
||||
internal cache. The cache will simply ignore any calls trying to
|
||||
append the core image.
|
||||
|
||||
.. versionadded:: 1.6.0
|
||||
|
||||
:attr:`nocache` is a :class:`~kivy.properties.BooleanProperty` and defaults
|
||||
to False.
|
||||
'''
|
||||
|
||||
def get_norm_image_size(self):
|
||||
if not self.texture:
|
||||
return list(self.size)
|
||||
ratio = self.image_ratio
|
||||
w, h = self.size
|
||||
tw, th = self.texture.size
|
||||
|
||||
# ensure that the width is always maximized to the container width
|
||||
if self.allow_stretch:
|
||||
if not self.keep_ratio:
|
||||
return [w, h]
|
||||
iw = w
|
||||
else:
|
||||
iw = min(w, tw)
|
||||
# calculate the appropriate height
|
||||
ih = iw / ratio
|
||||
# if the height is too higher, take the height of the container
|
||||
# and calculate appropriate width. no need to test further. :)
|
||||
if ih > h:
|
||||
if self.allow_stretch:
|
||||
ih = h
|
||||
else:
|
||||
ih = min(h, th)
|
||||
iw = ih * ratio
|
||||
return [iw, ih]
|
||||
|
||||
norm_image_size = AliasProperty(get_norm_image_size,
|
||||
bind=('texture', 'size', 'allow_stretch',
|
||||
'image_ratio', 'keep_ratio'),
|
||||
cache=True)
|
||||
'''Normalized image size within the widget box.
|
||||
|
||||
This size will always fit the widget size and will preserve the image
|
||||
ratio.
|
||||
|
||||
:attr:`norm_image_size` is an :class:`~kivy.properties.AliasProperty` and
|
||||
is read-only.
|
||||
'''
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._coreimage = None
|
||||
self._loops = 0
|
||||
update = self.texture_update
|
||||
fbind = self.fbind
|
||||
fbind('source', update)
|
||||
fbind('mipmap', update)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def texture_update(self, *largs):
|
||||
self.set_texture_from_resource(self.source)
|
||||
|
||||
def set_texture_from_resource(self, resource):
|
||||
if not resource:
|
||||
self._clear_core_image()
|
||||
return
|
||||
source = resource_find(resource)
|
||||
if not source:
|
||||
Logger.error('Image: Not found <%s>' % resource)
|
||||
self._clear_core_image()
|
||||
return
|
||||
if self._coreimage:
|
||||
self._coreimage.unbind(on_texture=self._on_tex_change)
|
||||
try:
|
||||
self._coreimage = image = CoreImage(
|
||||
source,
|
||||
mipmap=self.mipmap,
|
||||
anim_delay=self.anim_delay,
|
||||
keep_data=self.keep_data,
|
||||
nocache=self.nocache
|
||||
)
|
||||
except Exception:
|
||||
Logger.error('Image: Error loading <%s>' % resource)
|
||||
self._clear_core_image()
|
||||
image = self._coreimage
|
||||
if image:
|
||||
image.bind(on_texture=self._on_tex_change)
|
||||
self.texture = image.texture
|
||||
|
||||
def on_anim_delay(self, instance, value):
|
||||
if self._coreimage is None:
|
||||
return
|
||||
self._coreimage.anim_delay = value
|
||||
if value < 0:
|
||||
self._coreimage.anim_reset(False)
|
||||
|
||||
def on_texture(self, instance, value):
|
||||
self.texture_size = value.size if value else [0, 0]
|
||||
|
||||
def _clear_core_image(self):
|
||||
if self._coreimage:
|
||||
self._coreimage.unbind(on_texture=self._on_tex_change)
|
||||
self.texture = None
|
||||
self._coreimage = None
|
||||
self._loops = 0
|
||||
|
||||
def _on_tex_change(self, *largs):
|
||||
# update texture from core image
|
||||
self.texture = self._coreimage.texture
|
||||
ci = self._coreimage
|
||||
if self.anim_loop and ci._anim_index == len(ci._image.textures) - 1:
|
||||
self._loops += 1
|
||||
if self.anim_loop == self._loops:
|
||||
ci.anim_reset(False)
|
||||
self._loops = 0
|
||||
|
||||
def reload(self):
|
||||
'''Reload image from disk. This facilitates re-loading of
|
||||
images from disk in case the image content changes.
|
||||
|
||||
.. versionadded:: 1.3.0
|
||||
|
||||
Usage::
|
||||
|
||||
im = Image(source = '1.jpg')
|
||||
# -- do something --
|
||||
im.reload()
|
||||
# image will be re-loaded from disk
|
||||
|
||||
'''
|
||||
self.remove_from_cache()
|
||||
old_source = self.source
|
||||
self.source = ''
|
||||
self.source = old_source
|
||||
|
||||
def remove_from_cache(self):
|
||||
'''Remove image from cache.
|
||||
|
||||
.. versionadded:: 2.0.0
|
||||
'''
|
||||
if self._coreimage:
|
||||
self._coreimage.remove_from_cache()
|
||||
|
||||
def on_nocache(self, *args):
|
||||
if self.nocache:
|
||||
self.remove_from_cache()
|
||||
if self._coreimage:
|
||||
self._coreimage._nocache = True
|
||||
|
||||
|
||||
class AsyncImage(Image):
|
||||
'''Asynchronous Image class. See the module documentation for more
|
||||
information.
|
||||
|
||||
.. note::
|
||||
|
||||
The AsyncImage is a specialized form of the Image class. You may
|
||||
want to refer to the :mod:`~kivy.loader` documentation and in
|
||||
particular, the :class:`~kivy.loader.ProxyImage` for more detail
|
||||
on how to handle events around asynchronous image loading.
|
||||
|
||||
.. note::
|
||||
|
||||
AsyncImage currently does not support properties
|
||||
:attr:`anim_loop` and :attr:`mipmap` and setting those properties will
|
||||
have no effect.
|
||||
'''
|
||||
|
||||
__events__ = ('on_error', 'on_load')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._found_source = None
|
||||
self._coreimage = None
|
||||
global Loader
|
||||
if not Loader:
|
||||
from kivy.loader import Loader
|
||||
self.fbind('source', self._load_source)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def _load_source(self, *args):
|
||||
source = self.source
|
||||
if not source:
|
||||
self._clear_core_image()
|
||||
return
|
||||
if not self.is_uri(source):
|
||||
source = resource_find(source)
|
||||
if not source:
|
||||
Logger.error('AsyncImage: Not found <%s>' % self.source)
|
||||
self._clear_core_image()
|
||||
return
|
||||
self._found_source = source
|
||||
self._coreimage = image = Loader.image(
|
||||
source,
|
||||
nocache=self.nocache,
|
||||
mipmap=self.mipmap,
|
||||
anim_delay=self.anim_delay
|
||||
)
|
||||
image.bind(
|
||||
on_load=self._on_source_load,
|
||||
on_error=self._on_source_error,
|
||||
on_texture=self._on_tex_change
|
||||
)
|
||||
self.texture = image.texture
|
||||
|
||||
def _on_source_load(self, value):
|
||||
image = self._coreimage.image
|
||||
if not image:
|
||||
return
|
||||
self.texture = image.texture
|
||||
self.dispatch('on_load')
|
||||
|
||||
def _on_source_error(self, instance, error=None):
|
||||
self.dispatch('on_error', error)
|
||||
|
||||
def on_error(self, error):
|
||||
pass
|
||||
|
||||
def on_load(self, *args):
|
||||
pass
|
||||
|
||||
def is_uri(self, filename):
|
||||
proto = filename.split('://', 1)[0]
|
||||
return proto in ('http', 'https', 'ftp', 'smb')
|
||||
|
||||
def _clear_core_image(self):
|
||||
if self._coreimage:
|
||||
self._coreimage.unbind(on_load=self._on_source_load)
|
||||
super()._clear_core_image()
|
||||
self._found_source = None
|
||||
|
||||
def _on_tex_change(self, *largs):
|
||||
if self._coreimage:
|
||||
self.texture = self._coreimage.texture
|
||||
|
||||
def texture_update(self, *largs):
|
||||
pass
|
||||
|
||||
def remove_from_cache(self):
|
||||
if self._found_source:
|
||||
Loader.remove_from_cache(self._found_source)
|
||||
super().remove_from_cache()
|
||||
Reference in New Issue
Block a user