File: fader.py

package info (click to toggle)
linux-show-player 0.5.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,896 kB
  • sloc: python: 12,408; sh: 154; makefile: 17; xml: 8
file content (156 lines) | stat: -rw-r--r-- 4,988 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# -*- coding: utf-8 -*-
#
# This file is part of Linux Show Player
#
# Copyright 2012-2017 Francesco Ceruti <ceppofrancy@gmail.com>
#
# Linux Show Player 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 3 of the License, or
# (at your option) any later version.
#
# Linux Show Player 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 Linux Show Player.  If not, see <http://www.gnu.org/licenses/>.

from threading import Event

from lisp.core.decorators import locked_method
from lisp.core.fade_functions import ntime, FadeInType, FadeOutType
from lisp.core.util import rsetattr, rgetattr


class Fader:
    """Allow to perform fades on "generic" objects attributes.

     * Fades have a resolution of `1-hundredth-of-second`
     * To be able to fade correctly the attribute must be numeric, if not, the
       `fade` function will fail
     * When fading, the `fade` function cannot be entered by other threads,
       if this happens the function will simply return immediately
     * To execute a fader, the `prepare` function must be called first,
       this will also stop the fader
     * After calling `prepare` the fader is considered as running
     * The `stop` function wait until all "pending" target changes are applied
     * Changing the target will also stop the fader
    """

    def __init__(self, target, attribute):
        """
        :param target: The target object
        :type target: object
        :param attribute: The target attribute (name) to be faded
        :type attribute: str
        """
        self._time = 0  # current fade time in hundredths-of-seconds
        self._target = target
        self._attribute = attribute

        self._is_ready = Event()
        self._is_ready.set()
        self._running = Event()
        self._running.set()
        self._pause = Event()
        self._pause.set()

    @property
    def target(self):
        return self._target

    @target.setter
    def target(self, target):
        self.stop()
        self._target = target

    @property
    def attribute(self):
        return self._attribute

    @attribute.setter
    def attribute(self, target_property):
        self.stop()
        self._attribute = target_property

    def prepare(self):
        self.stop()

        self._running.clear()
        self._is_ready.clear()

    @locked_method(blocking=False)
    def fade(self, duration, to_value, fade_type):
        """
        :param duration: How much the fade should be long (in seconds)
        :type duration: float
        :param to_value: The value to reach
        :type to_value: float
        :param fade_type: The fade type
        :type fade_type: FadeInType | FadeOutType

        :return: False if the fade as been interrupted, True otherwise
        :rtype: bool
        """
        if duration <= 0:
            return

        if not isinstance(fade_type, (FadeInType, FadeOutType)):
            raise AttributeError(
                'fade_type must be one of FadeInType or FadeOutType member,'
                'not {}'.format(fade_type.__class__.__name__))

        try:
            self._time = 0
            begin = 0
            functor = fade_type.value
            duration = int(duration * 100)
            base_value = rgetattr(self._target, self._attribute)
            value_diff = to_value - base_value

            if value_diff == 0:
                return

            while self._time <= duration and not self._running.is_set():
                rsetattr(self._target,
                         self._attribute,
                         functor(ntime(self._time, begin, duration),
                                 value_diff,
                                 base_value))

                self._time += 1
                self._running.wait(0.01)
                self._pause.wait()
        finally:
            interrupted = self._running.is_set()
            self._running.set()
            self._is_ready.set()
            self._time = 0

        return not interrupted

    def stop(self):
        if not self._running.is_set() or not self._pause.is_set():
            self._running.set()
            self._pause.set()
            self._is_ready.wait()

    def pause(self):
        if self.is_running():
            self._pause.clear()

    def restart(self):
        # TODO: change to resume
        self._pause.set()

    def is_paused(self):
        return not self._pause.is_set()

    def is_running(self):
        return not self._running.is_set() and not self.is_paused()

    def current_time(self):
        # Return the time in millisecond
        return self._time * 10