Package dtk :: Package ui :: Module animation

Source Code for Module dtk.ui.animation

  1  #! /usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3   
  4  # Copyright (C) 2011 ~ 2012 Deepin, Inc. 
  5  #               2011 ~ 2012 Xia Bin 
  6  # 
  7  # Author:     Xia Bin <xiabin@gmail.com> 
  8  # Maintainer: Xia Bin <xiabin@gmail.com> 
  9  # 
 10  # This program is free software: you can redistribute it and/or modify 
 11  # it under the terms of the GNU General Public License as published by 
 12  # the Free Software Foundation, either version 3 of the License, or 
 13  # any later version. 
 14  # 
 15  # This program is distributed in the hope that it will be useful, 
 16  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 17  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 18  # GNU General Public License for more details. 
 19  # 
 20  # You should have received a copy of the GNU General Public License 
 21  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 22   
 23  import copy 
 24  import gobject 
 25   
26 -def LinerInterpolator(factor, lower, upper):
27 ''' 28 Linear interpolator 29 30 @param factor: the current factor 31 @param lower: the init lower value 32 @param lower: the init upper value 33 @return: the calculated value 34 ''' 35 return factor * (upper - lower)
36 -def RandomInterpolator(base, offset, *args):
37 ''' 38 Random interpolator 39 40 @param base: the base value used to calculate result value 41 @param offset: the offset apply to base. 42 @return: the random value based on 'base' and 'offset' 43 ''' 44 45 import random 46 return random.randint(base-offset/2, base+offset/2)
47 48
49 -class Animation:
50 ''' 51 The animation class used to convenient production special effects. 52 53 @undocumented: init 54 @undocumented: init_all 55 @undocumented: compute 56 '''
57 - def __init__(self, 58 widgets, 59 property, 60 duration, 61 ranges, 62 interpolator=LinerInterpolator, 63 stop_callback=None):
64 ''' 65 Initialize Animation class. 66 67 @param widgets: the widgets apply to this animation. the type of 68 this param is an gtk.Widget or an list of gtk.Widget. 69 70 @param property: the gtk.Widget's property used to do effect or an function to change the actual effect. 71 @param duration: the time of this effect to continued, the unit of time 72 is millisecond 73 @param ranges: the range of the property's value. the type of this param 74 is an [lower,upper] or ([lower, upper], [lower,upper]), this is decsion by the parameter of the 'widget' or 'widgets'. 75 @param interpolator: this is an function used to calculate the property value by the current time and value range. 76 @param stop_callback: the callback when this animation stop. 77 ''' 78 self.stop_callback = stop_callback 79 self.delay = 50 80 try: 81 widgets[0] 82 # FIXME: should use weakset or other things 83 self.widgets = widgets 84 #self.widgets = copy.weakref.WeakSet(widgets) 85 except: 86 self.widgets = [copy.weakref.ref(widgets)] 87 88 if isinstance(ranges, tuple): 89 self.ranges = ranges 90 else: 91 self.ranges = (ranges,) 92 93 94 self.duration = duration 95 self.interpolator = interpolator 96 self.time = 0 97 self.animation_id = None 98 self.start_id = None 99 self.other_concurent = [] 100 self.other_after = [] 101 102 def set_method1(*values): 103 for widget in self.widgets: 104 if isinstance(widget, copy.weakref.ref): 105 property(widget(), *values) 106 else: 107 property(widget, *values)
108 109 def set_method2(*values): 110 for widget in self.widgets: 111 if isinstance(widget, copy.weakref.ref): 112 widget().set_property(property, *values) 113 else: 114 widget.set_property(property, *values)
115 116 117 if callable(property): 118 self.set_method = set_method1 119 else: 120 self.set_method = set_method2 121
122 - def set_delay(self, delay):
123 ''' 124 Set the delay time of before the start do effect. 125 126 @param delay: the time of dealy, unit of time is millisecond 127 ''' 128 self.delay = delay
129
130 - def init(self, values=None):
131 if isinstance(values, list): 132 self.set_method(*values) 133 else: 134 self.set_method(values) 135 self.time = 0
136
137 - def init_all(self, values):
138 if isinstance(values, list): 139 values.reverse() 140 self.init(values.pop()) 141 for o in self.other_concurent: 142 value = values.pop() 143 o.init(value) 144 else: 145 raise Warning("init_all should init multi animation")
146
147 - def start_after(self, time):
148 ''' 149 Start the animation after the dealy time. 150 or you can use Animation.set_delay function. 151 152 @param time: the time of dealy, unit of time is millisecond 153 ''' 154 if self.start_id: 155 gobject.source_remove(self.start_id) 156 self.start_id = gobject.timeout_add(time, self.start) 157 for o in self.other_concurent: 158 o.start_after(time)
159
160 - def start(self):
161 ''' 162 Start the animation object. 163 ''' 164 self.time = 0 165 self.animation_id = gobject.timeout_add(self.delay, self.compute) 166 for o in self.other_concurent: 167 o.start() 168 return False
169
170 - def stop(self):
171 ''' 172 stop immediately the animation object 173 ''' 174 if self.animation_id: 175 gobject.source_remove(self.animation_id) 176 if self.start_id: 177 gobject.source_remove(self.start_id) 178 179 for o in self.other_concurent: 180 o.stop() 181 182 # Stop callback. 183 if self.stop_callback: 184 self.stop_callback()
185
186 - def compute(self):
187 if self.time >= self.duration+self.delay: 188 # Stop callback. 189 if self.stop_callback: 190 self.stop_callback() 191 return False 192 193 values = [] 194 for r in self.ranges: 195 factor = float(self.time) / self.duration 196 value = self.interpolator(factor, r[0], r[1]) 197 values.append(r[0]+value) 198 199 self.set_method(*values) 200 201 for o in self.other_concurent: 202 o.compute() 203 204 self.time += self.delay 205 206 return True
207
208 - def __mul__(self, other):
209 ''' 210 Overload the '*' operator to link two or more animation object. 211 the animation's effect is happend parallel. 212 @param other: the right hand side animation class. 213 @return: the new animation class with the two operator animation's effect. 214 ''' 215 r = copy.deepcopy(self) 216 r.other_concurent.append(other) 217 return r
218
219 - def __add__(self, other):
220 raise NotImplemented
221 222 if __name__ == "__main__": 223 import gtk 224 win = gtk.Window() 225 win.set_position(gtk.WIN_POS_CENTER) 226 227 box = gtk.VBox() 228 229 ani1 = Animation(win, lambda widget, v1, v2: widget.move(int(v1), int(v2)), 1000, ([200, 400], [200, 400])) 230 b1 = gtk.Button("moving....(set multip value)") 231 b1.connect('clicked', lambda w: ani1.start()) 232 box.add(b1) 233 234 ani2 = Animation(win, "opacity", 1000, [0, 1]) 235 b2 = gtk.Button("opacity(set single value)") 236 b2.connect('clicked', lambda w: ani2.start()) 237 box.add(b2) 238 239 ani3 = ani2 * ani1 # animation composite 240 b3 = gtk.Button("composited animation") 241 b3.connect('clicked', lambda w: ani3.start()) 242 box.add(b3) 243 244 ani4 = Animation(win, 245 lambda w, v1, v2: w.move(w.get_position()[0]+int(v1), w.get_position()[1]+int(v2)), 246 800, ([0, 0], [0,0]), lambda *args : RandomInterpolator(00, 20)) 247 b4 = gtk.Button("vibration") 248 b4.connect('clicked', lambda w: ani4.start()) 249 box.add(b4) 250 251 ani5 = Animation(win, lambda w, v1, v2: w.resize(int(v1), int(v2)), 300, ([20, 300], [20, 300])) 252 b5 = gtk.Button("smaller") 253 b5.connect('clicked', lambda w: ani5.start()) 254 box.add(b5) 255 256 win.add(box) 257 win.show_all() 258 win.connect('destroy', gtk.main_quit) 259 win.connect_after('show', lambda w: ani3.start()) 260 261 gtk.main() 262