# -*- coding: utf-8 -*-
#
# media_overlays/gif.py
#
# Copyright 2018 Cimbali <me@cimba.li>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
#
"""
:mod:`pympress.media_overlays.gif` -- widget to play gif images as videos
-------------------------------------------------------------------------
"""
import logging
logger = logging.getLogger(__name__)
import gi
import cairo
gi.require_version('Gtk', '3.0')
from gi.repository import Gdk, GLib, GdkPixbuf
from pympress.media_overlays import base
[docs]
class GifOverlay(base.VideoOverlay):
""" A simple overlay mimicking the functionality of showing videos, but showing gifs instead.
"""
#: A :class:`~GdkPixbuf.PixbufAnimation` containing all the frames and their timing for the displayed gif
anim = None
#: A :class:`~GdkPixbuf.PixbufAnimationIter` which will provide the timely access to the frames in `~anim`
anim_iter = None
#: A `tuple` of (`int`, `int`) indicating the size of the bounding box of the gif
base_size = None
#: The :class:`~cairo.Matrix` defining the zoom & shift to scale the gif
transform = None
def __init__(self, *args, **kwargs):
super(GifOverlay, self).__init__(*args, **kwargs)
# override: no toolbar or interactive stuff for a gif, replace the whole widget area with a GdkPixbuf
self.autoplay = True
self.toolbar.set_visible(False)
# we'll manually draw on the movie zone
self.movie_zone.connect('draw', self.draw)
self.movie_zone.connect('configure-event', self.set_transform)
def _set_file(self, filepath):
""" Sets the media file to be played by the widget.
Args:
filepath (`pathlib.Path`): The path to the media file path
"""
self.anim = GdkPixbuf.PixbufAnimation.new_from_file(str(filepath))
self.base_size = (self.anim.get_width(), self.anim.get_height())
self.anim_iter = self.anim.get_iter(None)
self.set_transform()
self.advance_gif()
[docs]
def draw(self, widget, ctx):
""" Simple resized drawing: get the pixbuf, set the transform, draw the image.
"""
if self.anim_iter is None:
return False
try:
ctx.transform(self.transform)
Gdk.cairo_set_source_pixbuf(ctx, self.anim_iter.get_pixbuf(), 0, 0)
ctx.paint()
except cairo.Error:
logger.error(_('Cairo can not draw gif'), exc_info = True)
[docs]
def advance_gif(self):
""" Advance the gif, queue redrawing if the frame changed, and schedule the next frame.
"""
if self.anim_iter.advance():
self.movie_zone.queue_draw()
delay = self.anim_iter.get_delay_time()
if delay >= 0:
GLib.timeout_add(delay, self.advance_gif)
[docs]
def do_set_time(self, t):
""" Set the player at time t.
Should run on the main thread to ensure we avoid reentrency problems.
Args:
t (`int`): the timestamp, in ms
Returns:
`bool`: `True` iff this function should be run again (:meth:`~GLib.idle_add` convention)
"""
start = GLib.TimeVal()
GLib.DateTime.new_now_local().to_timeval(start)
start.add(-t)
self.anim_iter = self.anim.get_iter(start)
self.advance_gif()
return False
# a bunch of inherited functions that do nothing, for gifs
[docs]
def mute(self, *args): pass
[docs]
def is_playing(self): return True
[docs]
def do_stop(self): pass
[docs]
def do_play(self): return False
[docs]
def do_play_pause(self): return False
[docs]
@classmethod
def setup_backend(cls):
""" Returns the name of this backend.
"""
return _('GdkPixbuf gif player')