/usr/share/pyshared/pgm/timing/implicit.py is in python-pgm 0.3.12-2.1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | # -*- mode: python; coding: utf-8 -*-
#
# Pigment Python tools
#
# Copyright © 2006, 2007, 2008 Fluendo Embedded S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import os, pgm, gobject
from pgm.timing import controller, modifier, keyframe
from pgm.utils import classinit
# Repeat behavior constants
FORWARD, REVERSE = 0, 1
# Repeat constant
INFINITE = -1
# Transformation constants
LINEAR, ACCELERATE, DECELERATE, SMOOTH = 5, 6, 7, 8
# Implicit mode
REPLACE, APPEND = 0, 1
# helper for implicitly animating pgm.Drawables
DRAWABLE_ATTRIBUTES = ("size",
"width",
"height",
"position",
"x",
"y",
"z",
"rx",
"ry",
"rz",
"s",
"fg_color",
"fg_r",
"fg_g",
"fg_b",
"fg_a",
"bg_color",
"bg_r",
"bg_g",
"bg_b",
"bg_a",
"opacity")
class AnimatedObject(object):
"""
This is a proxy to any object that would like some of its attributes to be
implicitly animated when set.
@ivar passthrough: whether or not the object is animating proxied object's
variables; False by default
@type passthrough: bool
@ivar _animated_attributes: attributes of the proxied object that will be
implicitly animated
@type _animated_attributes: tuple of strings
@ivar _object: object that is proxied and implicitly animated
@type _object: mutable object
@ivar _current_controllers: controllers of the currently animated attributes
@type _current_controllers: dict of L{pgm.timing.controller.Controller}
@ivar _next_controllers: controllers that will be used next time the
attributes are animated
@type _next_controllers: dict of L{pgm.timing.controller.Controller}
"""
__metaclass__ = classinit.ClassInitMeta
__classinit__ = classinit.build_properties
def __init__(self, object, animated_attributes=DRAWABLE_ATTRIBUTES):
"""
Initialize the controllers for the next implicit animations that will
happen on object.
@param object: object to be proxied and implicitly
animated
@type object: mutable object
@param animated_attributes: attributes of the proxied object that will
be implicitly animated
@type animated_attributes: tuple of strings
"""
super(AnimatedObject, self).__init__()
self._object = object
self._current_controllers = {}
self._next_controllers = {}
self._animated_attributes = ()
# create default controllers
for attribute in animated_attributes:
self.add_animated_attribute(attribute)
self.mode = REPLACE
self.passthrough = False
def static_object__get(self):
return self._object
def setup_next_animations(self, attribute=None, duration=1000, resolution=17,
repeat_behavior=FORWARD, repeat_count=1,
end_behavior=controller.HOLD,
transformation=LINEAR, end_callback=None):
"""
Setup the next implicit animations for L{attribute}.
@param attribute: attribute for which the animation is setup
@type attribute: string
@param resolution: resolution of the implicit animation
@type resolution: float
@param duration: duration of the implicit animation
@type duration: int
FIXME: documentation
"""
if attribute != None:
ctrl = controller.Controller(duration = duration,
resolution = resolution,
repeat_behavior = repeat_behavior,
repeat_count = repeat_count,
end_behavior = end_behavior,
transformation = transformation,
modifiers = [],
end_callback = end_callback)
self._next_controllers[attribute] = ctrl
else:
for attribute in self._animated_attributes:
ctrl = controller.Controller(duration = duration,
resolution = resolution,
repeat_behavior = repeat_behavior,
repeat_count = repeat_count,
end_behavior = end_behavior,
transformation = transformation,
modifiers = [],
end_callback = end_callback)
self._next_controllers[attribute] = ctrl
def update_animation_settings(self, attribute=None, duration=None,
resolution=None, repeat_behavior=None,
repeat_count=None, transformation=None,
end_behavior=None, end_callback=-1):
"""
Update the animation parameters for one animated attribute (if
specified) or all of them, if L{attribute} is set to None.
@keyword attribute: the property for which we want to update
the settings
@type attribute: string or None
@keyword duration: new duration of animation, in milliseconds
@type duration: int
@keyword resolution: new resolution of the animation
@type resolution: float
@keyword repeat_behavior: new repeat behavior of the animation
@type repeat_behavior: int
@keyword repeat_count: new repeat count of the animation
@type repeat_count: int
@keyword transformation: new transformation to apply to the animation
@type transformation: int
@keyword end_callback: new callback to call at the end of animation
@type end_callback: callable
"""
def update_controller(controller):
if duration != None:
controller.duration = duration
if end_behavior != None:
controller.end_behavior = end_behavior
if resolution != None:
controller.resolution = resolution
if repeat_behavior != None:
controller.repeat_behavior = repeat_behavior
if repeat_count != None:
controller.repeat_count = repeat_count
if transformation != None:
controller.transformation = transformation
if end_callback != -1:
controller.end_callback = end_callback
if attribute != None:
update_controller(self._current_controllers.get(attribute))
else:
for controller in self._current_controllers.values():
update_controller(controller)
def stop_animations(self):
"""
Stop the animations for all the attributes.
"""
for controller in self._current_controllers.values():
controller.stop()
controller.modifiers = []
def stop_animation_for_attribute(self, attribute):
"""
Stop the animation for a given attribute.
@param attribute: attribute for which animations should be stopped
@type attribute: string
"""
controller = self._current_controllers[attribute]
controller.stop()
controller.modifiers = []
def add_animated_attribute(self, attribute):
"""
Setup L{attribute} to be implicitly animated. Further assignments
to this attribute will provoke animations.
@param attribute: attribute to setup for implicit animations
@type attribute: string
"""
self._animated_attributes += (attribute,)
ctrl = controller.Controller(resolution = 17,
end_behavior = controller.HOLD,
transformation = controller.LINEAR,
modifiers = [])
self._current_controllers[attribute] = ctrl
def _animate_attribute_new_animation(self, attribute, target_values):
# get the current value of the attribute from the proxied object
current_value = getattr(self._object, attribute)
# retrieve the currrent controller for the attribute
ctrl = self._current_controllers[attribute]
if ctrl.started:
ctrl.stop()
# setup timeline and modifier for the new animation
nb_target_values = len(target_values)
time_step = 1.0/nb_target_values
timeline = [keyframe.KeyFrame(target_values[i], time_step*(i+1)) for i in
xrange(nb_target_values)]
timeline.insert(0, keyframe.KeyFrame(current_value, 0.0))
# check if there is a new controller ready for the attribute
if self._next_controllers.has_key(attribute):
# use the new controller
ctrl = self._next_controllers[attribute]
self._current_controllers[attribute] = ctrl
del self._next_controllers[attribute]
if len(ctrl.modifiers) > 0:
mod = ctrl.modifiers[0]
mod.timeline = timeline
else:
mod = modifier.Modifier([self._object],
attribute,
timeline)
ctrl.modifiers = [mod]
# start the animation
ctrl.start()
def _animate_attribute_new_target(self, attribute, target_value):
# get the current value of the attribute from the proxied object
current_value = getattr(self._object, attribute)
# retrieve the current controller for the attribute
ctrl = self._current_controllers[attribute]
if ctrl.started:
# do not do anything if the new target is the same as the old target
if target_value == ctrl.modifiers[0].timeline[-1].value:
return
else:
# modify the timeline and restart the animation
ctrl.stop()
ctrl.modifiers[0].timeline[0].value = current_value
ctrl.modifiers[0].timeline[-1].value = target_value
ctrl.start()
else:
# The animation is always triggered even if the target value is the
# same as the current value. It has not always been the case and
# led to long debugging sessions...
self._animate_attribute_new_animation(attribute, [target_value])
def _animate_attribute_add_target(self, attribute, target_value):
# get the current value of the attribute from the proxied object
current_value = getattr(self._object, attribute)
# retrieve the current controller for the attribute
ctrl = self._current_controllers[attribute]
if ctrl.started:
# modify the timeline
time = ctrl._compute_factor()
#ctrl.stop()
if time == 0.0:
i = 1
else:
i = ctrl.modifiers[0].next_keyframe_index(time)
targets = [key.value for key in ctrl.modifiers[0].timeline[i:]]
targets.append(target_value)
self._animate_attribute_new_animation(attribute, targets)
"""
# setup timeline and modifier for the new animation
nb_targets = len(targets)
time_step = 1.0/nb_targets
timeline = [keyframe.KeyFrame(targets[i], time_step*(i+1)) for i in
range(nb_targets)]
timeline.insert(0, keyframe.KeyFrame(current_value, 0.0))
ctrl.modifiers[0].timeline = timeline
ctrl.start()
"""
else:
self._animate_attribute_new_animation(attribute, [target_value])
def __setattr__(self, attribute, value):
if attribute == "_object" or \
attribute == "_current_controllers" or \
attribute == "_next_controllers" or \
attribute == "_animated_attributes" or \
attribute == "mode" or \
attribute == "passthrough":
# if self._object is set, set it locally
super(AnimatedObject, self).__setattr__(attribute, value)
else:
if not self.passthrough and attribute in self._animated_attributes:
# if an animated attribute is to be set, animate it
if not isinstance(value, list):
if self.mode == REPLACE:
self._animate_attribute_new_target(attribute, value)
elif self.mode == APPEND:
self._animate_attribute_add_target(attribute, value)
else:
# FIXME: could also use add_target
self._animate_attribute_new_animation(attribute, value)
else:
# for any other attribute set, set it to the proxied object
if isinstance(value, list):
value = value[-1]
setattr(self._object, attribute, value)
def __getattr__(self, attribute):
# for any attribute accessed not found locally
# retrieve it from the proxied object
if attribute in self._animated_attributes:
# if an animated attribute is to be retrieved, do not return the
# real value but the target value
ctrl = self._current_controllers[attribute]
if ctrl.started:
return ctrl.modifiers[0].timeline[-1].value
else:
return getattr(self._object, attribute)
else:
return getattr(self._object, attribute)
def is_animated(self, attribute = None):
"""
Returns True if L{attribute} is currently being animated, False
otherwise.
If no attribute is specified, return True if any of the attributes is
currently animated.
@param attribute: attribute to check for
@type attribute: string
@rtype: boolean
"""
if attribute != None:
controller = self._current_controllers[attribute]
return controller.started
else:
for controller in self._current_controllers.values():
if controller.started:
return True
return False
|