[hamster-applet] synced graphics and tweener with hamster_experiments
- From: Toms Baugis <tbaugis src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [hamster-applet] synced graphics and tweener with hamster_experiments
- Date: Mon, 11 Jan 2010 21:40:59 +0000 (UTC)
commit d71777a621b5bd1397dfb510dcaf337f4a670674
Author: Toms Bauģis <toms baugis gmail com>
Date: Mon Jan 11 21:01:45 2010 +0000
synced graphics and tweener with hamster_experiments
hamster/charting.py | 3 +-
hamster/graphics.py | 48 +++++-
hamster/pytweener.py | 345 ++++++++++++++++----------------------------
hamster/widgets/dayline.py | 2 +-
4 files changed, 167 insertions(+), 231 deletions(-)
---
diff --git a/hamster/charting.py b/hamster/charting.py
index cd8f14c..7522dbe 100644
--- a/hamster/charting.py
+++ b/hamster/charting.py
@@ -240,8 +240,7 @@ class Chart(graphics.Area):
bars[i] = Bar(new_values[i], 0)
else:
bars[i].value = new_values[i]
- for tween in self.tweener.getTweensAffectingObject(bars[i]):
- self.tweener.removeTween(tween)
+ self.tweener.killTweensOf(bars[i])
self.tweener.addTween(bars[i], size = bars[i].value / float(max_value))
return bars
diff --git a/hamster/graphics.py b/hamster/graphics.py
index 6897e04..0186800 100644
--- a/hamster/graphics.py
+++ b/hamster/graphics.py
@@ -55,7 +55,10 @@ class Area(gtk.DrawingArea):
"expose-event": "override",
"configure_event": "override",
"mouse-over": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )),
+ "button-press": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )),
"button-release": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )),
+ "mouse-move": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)),
+ "mouse-click": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )),
}
def __init__(self):
@@ -66,6 +69,7 @@ class Area(gtk.DrawingArea):
| gtk.gdk.BUTTON_RELEASE_MASK
| gtk.gdk.POINTER_MOTION_MASK
| gtk.gdk.POINTER_MOTION_HINT_MASK)
+ self.connect("button_press_event", self.__on_button_press)
self.connect("button_release_event", self.__on_button_release)
self.connect("motion_notify_event", self.__on_mouse_move)
self.connect("leave_notify_event", self.__on_mouse_out)
@@ -81,6 +85,8 @@ class Area(gtk.DrawingArea):
self.framerate = 80
self.last_frame_time = None
self.__animating = False
+
+ self.mouse_drag = (None, None)
def on_expose(self):
""" on_expose event is where you hook in all your drawing
@@ -111,12 +117,14 @@ class Area(gtk.DrawingArea):
return self.__animating
- def animate(self, object, params = {}, duration = None, easing = None, callback = None):
+ def animate(self, object, params = {}, duration = None, easing = None, callback = None, instant = True):
if duration: params["tweenTime"] = duration # if none will fallback to tweener default
if easing: params["tweenType"] = easing # if none will fallback to tweener default
if callback: params["onCompleteFunction"] = callback
self.tweener.addTween(object, **params)
- self.redraw_canvas()
+
+ if instant:
+ self.redraw_canvas()
""" drawing on canvas bits """
@@ -211,9 +219,6 @@ class Area(gtk.DrawingArea):
""" mouse events """
def __on_mouse_move(self, area, event):
- if not self.mouse_regions:
- return
-
if event.is_hint:
x, y, state = event.window.get_pointer()
else:
@@ -221,6 +226,11 @@ class Area(gtk.DrawingArea):
y = event.y
state = event.state
+ self.emit("mouse-move", (x, y), state)
+
+ if not self.mouse_regions:
+ return
+
mouse_regions = []
for region in self.mouse_regions:
if region[0] < x < region[2] and region[1] < y < region[3]:
@@ -240,14 +250,38 @@ class Area(gtk.DrawingArea):
self.__prev_mouse_regions = None
self.emit("mouse-over", [])
- def __on_button_release(self, area, event):
+
+ def __on_button_press(self, area, event):
+ x = event.x
+ y = event.y
+ state = event.state
+ self.mouse_drag = (x, y)
+
if not self.mouse_regions:
return
+ mouse_regions = []
+ for region in self.mouse_regions:
+ if region[0] < x < region[2] and region[1] < y < region[3]:
+ mouse_regions.append(region[4])
+
+ if mouse_regions:
+ self.emit("button-press", mouse_regions)
+
+ def __on_button_release(self, area, event):
x = event.x
y = event.y
state = event.state
+ drag_distance = 5
+ if self.mouse_drag and (self.mouse_drag[0] - x) ** 2 + (self.mouse_drag[1] - y) ** 2 < drag_distance ** 2:
+ #if the drag is less than the drag distance, then we have a click
+ self.emit("mouse-click", (x,y))
+ self.mouse_drag = None
+
+ if not self.mouse_regions:
+ return
+
mouse_regions = []
for region in self.mouse_regions:
if region[0] < x < region[2] and region[1] < y < region[3]:
@@ -256,8 +290,6 @@ class Area(gtk.DrawingArea):
if mouse_regions:
self.emit("button-release", mouse_regions)
-
-
""" simple example """
class SampleArea(Area):
diff --git a/hamster/pytweener.py b/hamster/pytweener.py
index 80340c0..575dd31 100644
--- a/hamster/pytweener.py
+++ b/hamster/pytweener.py
@@ -6,17 +6,17 @@
#
# Released under M.I.T License - see above url
# Python version by Ben Harling 2009
-# Performance optimizations by Toms Baugis 2010
+# All kinds of slashing and dashing by Toms Baugis 2010
import math
-class Tweener:
- def __init__(self, duration = 0.5, tween = None):
+class Tweener(object):
+ def __init__(self, default_duration = None, tween = None):
"""Tweener
This class manages all active tweens, and provides a factory for
creating and spawning tween motions."""
self.currentTweens = {}
self.defaultTweenType = tween or Easing.Cubic.easeInOut
- self.defaultDuration = duration or 1.0
+ self.defaultDuration = default_duration or 1.0
def hasTweens(self):
return len(self.currentTweens) > 0
@@ -41,24 +41,24 @@ class Tweener:
tweenTime = the duration of the motion
tweenType = one of the predefined tweening equations or your own function
- onCompleteFunction = specify a function to call on completion of the tween
- onUpdateFunction = specify a function to call every time the tween updates
+ onComplete = specify a function to call on completion of the tween
+ onUpdate = specify a function to call every time the tween updates
tweenDelay = specify a delay before starting.
"""
if "tweenTime" in kwargs:
t_time = kwargs.pop("tweenTime")
else: t_time = self.defaultDuration
-
+
if "tweenType" in kwargs:
t_type = kwargs.pop("tweenType")
else: t_type = self.defaultTweenType
- if "onCompleteFunction" in kwargs:
- t_completeFunc = kwargs.pop("onCompleteFunction")
+ if "onComplete" in kwargs:
+ t_completeFunc = kwargs.pop("onComplete")
else: t_completeFunc = None
- if "onUpdateFunction" in kwargs:
- t_updateFunc = kwargs.pop("onUpdateFunction")
+ if "onUpdate" in kwargs:
+ t_updateFunc = kwargs.pop("onUpdate")
else: t_updateFunc = None
if "tweenDelay" in kwargs:
@@ -82,9 +82,10 @@ class Tweener:
def killTweensOf(self, obj):
"""Stop tweening an object, without completing the motion
or firing the completeFunction"""
-
- for tween in self.currentTweens.get(obj, []):
- tween.complete = True
+ try:
+ del self.currentTweens[obj]
+ except:
+ pass
def finish(self):
@@ -98,86 +99,36 @@ class Tweener:
for obj in self.currentTweens.keys():
# updating tweens from last to first and deleting while at it
# in order to not confuse the index
- # TODO - should use generator instead?
for i, t in reversed(list(enumerate(self.currentTweens[obj]))):
t.update(timeSinceLastFrame)
if t.complete:
del self.currentTweens[obj][i]
-
- if not self.currentTweens[obj]:
- del self.currentTweens[obj]
-
-
+
+ if not self.currentTweens[obj]:
+ del self.currentTweens[obj]
class Tween(object):
- def __init__(self, obj, tduration, tweenType, completeFunction, updateFunction, delay, **kwargs):
- """Tween object:
- Can be created directly, but much more easily using Tweener.addTween( ... )
- """
- #print obj, tduration, kwargs
- self.duration = tduration
+ __slots__ = ['duration', 'delay', 'target', 'tween', 'tweenables', 'delta',
+ 'target', 'ease', 'tweenables', 'delta', 'completeFunction',
+ 'updateFunction', 'complete', 'paused']
+
+ def __init__(self, obj, duration, easing, on_complete, on_update, delay, **kwargs):
+ """Tween object use Tweener.addTween( ... ) to create"""
+ self.duration = duration
self.delay = delay
self.target = obj
- self.tween = tweenType
- self.tweenables = kwargs
+ self.ease = easing
+
+ # list of (property, start_value, end_value)
+ self.tweenables = [(k, self.target.__dict__[k], v) for k, v in kwargs.items()]
+
self.delta = 0
- self.completeFunction = completeFunction
- self.updateFunction = updateFunction
+ self.completeFunction = on_complete
+ self.updateFunction = on_update
self.complete = False
- self.tProps = []
- self.tFuncs = []
+
self.paused = self.delay > 0
- self.decodeArguments()
-
- def decodeArguments(self):
- """Internal setup procedure to create tweenables and work out
- how to deal with each"""
-
- if len(self.tweenables) == 0:
- # nothing to do
- print "TWEEN ERROR: No Tweenable properties or functions defined"
- self.complete = True
- return
-
- for k, v in self.tweenables.items():
-
- # check that its compatible
- if not hasattr( self.target, k):
- print "TWEEN ERROR: " + str(self.target) + " has no function " + k
- self.complete = True
- break
-
- prop = func = False
- startVal = 0
- newVal = v
-
- try:
- startVal = self.target.__dict__[k]
- prop = k
- except:
- func = getattr( self.target, k)
-
- if func:
- try:
- getFunc = getattr(self.target, funcName.replace("set", "get") )
- startVal = getFunc()
- except:
- # no start value, assume its 0
- # but make sure the start and change
- # dataTypes match :)
- startVal = newVal * 0
- tweenable = Tweenable( startVal, newVal - startVal)
- newFunc = [func, tweenable]
-
- self.tFuncs.append( newFunc )
-
-
- if prop:
- tweenable = Tweenable( startVal, newVal - startVal)
- newProp = [prop, tweenable]
- self.tProps.append( newProp )
-
-
+
def pause( self, numSeconds=-1 ):
"""Pause this tween
do tween.pause( 2 ) to pause for a specific time
@@ -191,9 +142,9 @@ class Tween(object):
self.paused=False
def update(self, ptime):
- """Update this tween with the time since the last frame
- if there is an update function, it is always called
- whether the tween is running or paused"""
+ """Update tween with the time since the last frame
+ if there is an update callback, it is always called
+ whether the tween is running or paused"""
if self.complete:
return
@@ -208,15 +159,14 @@ class Tween(object):
self.updateFunction()
return
- self.delta = min(self.delta + ptime, self.duration)
-
+ self.delta = self.delta + ptime
+ if self.delta > self.duration:
+ self.delta = self.duration
- for prop, tweenable in self.tProps:
- self.target.__dict__[prop] = self.tween( self.delta, tweenable.startValue, tweenable.change, self.duration )
- for func, tweenable in self.tFuncs:
- func( self.tween( self.delta, tweenable.startValue, tweenable.change, self.duration ) )
-
-
+
+ for prop, start_value, end_value in self.tweenables:
+ self.target.__dict__[prop] = self.ease(self.delta, start_value, end_value - start_value, self.duration)
+
if self.delta == self.duration:
self.complete = True
if self.completeFunction:
@@ -224,48 +174,6 @@ class Tween(object):
if self.updateFunction:
self.updateFunction()
-
-
-
- def getTweenable(self, name):
- """Return the tweenable values corresponding to the name of the original
- tweening function or property.
-
- Allows the parameters of tweens to be changed at runtime. The parameters
- can even be tweened themselves!
-
- eg:
-
- # the rocket needs to escape!! - we're already moving, but must go faster!
- twn = tweener.getTweensAffectingObject( myRocket )[0]
- tweenable = twn.getTweenable( "thrusterPower" )
- tweener.addTween( tweenable, change=1000.0, tweenTime=0.4, tweenType=tweener.IN_QUAD )
-
- """
- ret = None
- for n, f, t in self.tFuncs:
- if n == name:
- ret = t
- return ret
- for n, p, t in self.tProps:
- if n == name:
- ret = t
- return ret
- return ret
-
- def Remove(self):
- """Disables and removes this tween
- without calling the complete function"""
- self.complete = True
-
-
-class Tweenable:
- def __init__(self, start, change):
- """Tweenable:
- Holds values for anything that can be tweened
- these are normally only created by Tweens"""
- self.startValue = start
- self.change = change
"""Robert Penner's easing classes ported over from actionscript by Toms Baugis (at gmail com).
@@ -307,17 +215,17 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
-class Easing:
- class Back:
+class Easing(object):
+ class Back(object):
@staticmethod
def easeIn(t, b, c, d, s = 1.70158):
t = t / d
- return c * t**2 * ((s+1) * t - s) + b
+ return c * t * t * ((s+1) * t - s) + b
@staticmethod
def easeOut (t, b, c, d, s = 1.70158):
t = t / d - 1
- return c * (t**2 * ((s + 1) * t + s) + 1) + b
+ return c * (t * t * ((s + 1) * t + s) + 1) + b
@staticmethod
def easeInOut (t, b, c, d, s = 1.70158):
@@ -325,26 +233,26 @@ class Easing:
s = s * 1.525
if t < 1:
- return c * 0.5 * (t**2 * ((s + 1) * t - s)) + b
+ return c * 0.5 * (t * t * ((s + 1) * t - s)) + b
t = t - 2
- return c / 2 * (t**2 * ((s + 1) * t + s) + 2) + b
+ return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b
- class Bounce:
+ class Bounce(object):
@staticmethod
def easeOut (t, b, c, d):
t = t / d
if t < 1 / 2.75:
- return c * (7.5625 * t**2) + b
+ return c * (7.5625 * t * t) + b
elif t < 2 / 2.75:
t = t - 1.5 / 2.75
- return c * (7.5625 * t**2 + 0.75) + b
+ return c * (7.5625 * t * t + 0.75) + b
elif t < 2.5 / 2.75:
t = t - 2.25 / 2.75
- return c * (7.5625 * t**2 + .9375) + b
+ return c * (7.5625 * t * t + .9375) + b
else:
t = t - 2.625 / 2.75
- return c * (7.5625 * t**2 + 0.984375) + b
+ return c * (7.5625 * t * t + 0.984375) + b
@staticmethod
def easeIn (t, b, c, d):
@@ -359,49 +267,49 @@ class Easing:
- class Circ:
+ class Circ(object):
@staticmethod
def easeIn (t, b, c, d):
t = t / d
- return -c * (math.sqrt(1 - t**2) - 1) + b
+ return -c * (math.sqrt(1 - t * t) - 1) + b
@staticmethod
def easeOut (t, b, c, d):
t = t / d - 1
- return c * math.sqrt(1 - t**2) + b
+ return c * math.sqrt(1 - t * t) + b
@staticmethod
def easeInOut (t, b, c, d):
t = t / (d * 0.5)
if t < 1:
- return -c * 0.5 * (math.sqrt(1 - t**2) - 1) + b
+ return -c * 0.5 * (math.sqrt(1 - t * t) - 1) + b
t = t - 2
- return c*0.5 * (math.sqrt(1 - t**2) + 1) + b
+ return c*0.5 * (math.sqrt(1 - t * t) + 1) + b
- class Cubic:
+ class Cubic(object):
@staticmethod
def easeIn (t, b, c, d):
t = t / d
- return c * t**3 + b
+ return c * t * t * t + b
@staticmethod
def easeOut (t, b, c, d):
t = t / d - 1
- return c * (t**3 + 1) + b
+ return c * (t * t * t + 1) + b
@staticmethod
def easeInOut (t, b, c, d):
t = t / (d * 0.5)
if t < 1:
- return c * 0.5 * t**3 + b
+ return c * 0.5 * t * t * t + b
t = t - 2
- return c * 0.5 * (t**3 + 2) + b
+ return c * 0.5 * (t * t * t + 2) + b
- class Elastic:
+ class Elastic(object):
@staticmethod
def easeIn (t, b, c, d, a = 0, p = 0):
if t==0: return b
@@ -462,7 +370,7 @@ class Easing:
return a * math.pow(2, -10 * t) * math.sin((t * d - s) * (2 * math.pi) / p) * .5 + c + b
- class Expo:
+ class Expo(object):
@staticmethod
def easeIn(t, b, c, d):
if t == 0:
@@ -492,7 +400,7 @@ class Easing:
return c * 0.5 * (-math.pow(2, -10 * (t - 1)) + 2) + b
- class Linear:
+ class Linear(object):
@staticmethod
def easeNone(t, b, c, d):
return c * t / d + b
@@ -510,11 +418,11 @@ class Easing:
return c * t / d + b
- class Quad:
+ class Quad(object):
@staticmethod
def easeIn (t, b, c, d):
t = t / d
- return c * t**2 + b
+ return c * t * t + b
@staticmethod
def easeOut (t, b, c, d):
@@ -525,54 +433,54 @@ class Easing:
def easeInOut (t, b, c, d):
t = t / (d * 0.5)
if t < 1:
- return c * 0.5 * t**2 + b
+ return c * 0.5 * t * t + b
t = t - 1
return -c * 0.5 * (t * (t - 2) - 1) + b
- class Quart:
+ class Quart(object):
@staticmethod
def easeIn (t, b, c, d):
t = t / d
- return c * t**4 + b
+ return c * t * t * t * t + b
@staticmethod
def easeOut (t, b, c, d):
t = t / d - 1
- return -c * (t**4 - 1) + b
+ return -c * (t * t * t * t - 1) + b
@staticmethod
def easeInOut (t, b, c, d):
t = t / (d * 0.5)
if t < 1:
- return c * 0.5 * t**4 + b
+ return c * 0.5 * t * t * t * t + b
t = t - 2
- return -c * 0.5 * (t**4 - 2) + b
+ return -c * 0.5 * (t * t * t * t - 2) + b
- class Quint:
+ class Quint(object):
@staticmethod
def easeIn (t, b, c, d):
t = t / d
- return c * t**5 + b
+ return c * t * t * t * t * t + b
@staticmethod
def easeOut (t, b, c, d):
t = t / d - 1
- return c * (t**5 + 1) + b
+ return c * (t * t * t * t * t + 1) + b
@staticmethod
def easeInOut (t, b, c, d):
t = t / (d * 0.5)
if t < 1:
- return c * 0.5 * t**5 + b
+ return c * 0.5 * t * t * t * t * t + b
t = t - 2
- return c * 0.5 * (t**5 + 2) + b
+ return c * 0.5 * (t * t * t * t * t + 2) + b
- class Sine:
+ class Sine(object):
@staticmethod
def easeIn (t, b, c, d):
return -c * math.cos(t / d * (math.pi / 2)) + c + b
@@ -586,7 +494,7 @@ class Easing:
return -c * 0.5 * (math.cos(math.pi * t / d) - 1) + b
- class Strong:
+ class Strong(object):
@staticmethod
def easeIn(t, b, c, d):
return c * (t/d)**5 + b
@@ -600,53 +508,50 @@ class Easing:
t = t / (d * 0.5)
if t < 1:
- return c * 0.5 * t**5 + b
+ return c * 0.5 * t * t * t * t * t + b
t = t - 2
- return c * 0.5 * (t**5 + 2) + b
+ return c * 0.5 * (t * t * t * t * t + 2) + b
-class TweenTestObject:
- def __init__(self):
- self.pos = 20
- self.rot = 50
-
- def update(self):
- print self.pos, self.rot
-
- def setRotation(self, rot):
- self.rot = rot
-
- def getRotation(self):
- return self.rot
-
- def complete(self):
- print "I'm done tweening now mommy!"
-
-
-if __name__=="__main__":
- import time
- T = Tweener()
- tst = TweenTestObject()
- mt = T.addTween( tst, setRotation=500.0, tweenTime=2.5, tweenType=T.OUT_EXPO,
- pos=-200, tweenDelay=0.4, onCompleteFunction=tst.complete,
- onUpdateFunction=tst.update )
- s = time.clock()
- changed = False
- while T.hasTweens():
- tm = time.clock()
- d = tm - s
- s = tm
- T.update( d )
- if mt.delta > 1.0 and not changed:
-
- tweenable = mt.getTweenable( "setRotation" )
-
- T.addTween( tweenable, change=-1000, tweenTime=0.7 )
- T.addTween( mt, duration=-0.2, tweenTime=0.2 )
- changed = True
- #print mt.duration,
- print tst.getRotation(), tst.pos
- time.sleep(0.06)
- print tst.getRotation(), tst.pos
+
+class _PerformanceTester(object):
+ def __init__(self, a, b, c):
+ self.a = a
+ self.b = b
+ self.c = c
+
+if __name__ == "__main__":
+ import datetime as dt
+
+ tweener = Tweener()
+ objects = []
+ for i in range(10000):
+ objects.append(_PerformanceTester(i-100, i-100, i-100))
+
+
+ total = dt.datetime.now()
+
+ t = dt.datetime.now()
+ for i, o in enumerate(objects):
+ tweener.addTween(o, a = i, b = i, c = i, tweenTime = 1.0)
+ print "add", dt.datetime.now() - t
+
+ t = dt.datetime.now()
+
+ for i in range(10):
+ tweener.update(0.01)
+ print "update", dt.datetime.now() - t
+
+ t = dt.datetime.now()
+
+ for i in range(10):
+ for i, o in enumerate(objects):
+ tweener.killTweensOf(o)
+ tweener.addTween(o, a = i, b = i, c = i, tweenTime = 1.0)
+ print "kill-add", dt.datetime.now() - t
+
+ print "total", dt.datetime.now() - total
+
+
diff --git a/hamster/widgets/dayline.py b/hamster/widgets/dayline.py
index 8b0c259..1d73219 100644
--- a/hamster/widgets/dayline.py
+++ b/hamster/widgets/dayline.py
@@ -200,7 +200,7 @@ class DayLine(graphics.Area):
return delta.days * 24 * 60 + delta.seconds / 60
def scroll_to_range_start(self):
- self.tweener.removeTweeningFrom(self)
+ self.tweener.killTweensOf(self)
self.animate(self, {"range_start_int": int(time.mktime(self.range_start.timetuple())),
"tweenType": graphics.Easing.Expo.easeOut,
"tweenTime": 0.4})
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]