[billreminder/fresh] synced with hamster
- From: Toms Baugis <tbaugis src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [billreminder/fresh] synced with hamster
- Date: Wed, 20 Jan 2010 14:52:52 +0000 (UTC)
commit b95d9fbe1b7f2f4acc95a906ce28d73eb5d81692
Author: Toms Bauģis <toms baugis gmail com>
Date: Wed Jan 20 14:50:29 2010 +0000
synced with hamster
src/gui/widgets/charting.py | 307 +++++++++++++++++----------
src/gui/widgets/graphics.py | 131 ++++++++----
src/gui/widgets/pytweener.py | 494 +++++++++++++++++-------------------------
3 files changed, 483 insertions(+), 449 deletions(-)
---
diff --git a/src/gui/widgets/charting.py b/src/gui/widgets/charting.py
index cd8f14c..24ecd60 100644
--- a/src/gui/widgets/charting.py
+++ b/src/gui/widgets/charting.py
@@ -75,16 +75,16 @@ def get_limits(set, stack_subfactors = True):
min_value = max(row, min_value)
return min_value, max_value
-
+
class Bar(object):
def __init__(self, value, size = 0):
self.value = value
self.size = size
-
+
def __repr__(self):
return str((self.value, self.size))
-
+
class Chart(graphics.Area):
"""Chart constructor. Optional arguments:
@@ -146,34 +146,34 @@ class Chart(graphics.Area):
self.keys = []
self.data = None
self.stack_keys = []
-
+
self.key_colors = {} # key:color dictionary. if key's missing will grab basecolor
self.stack_key_colors = {} # key:color dictionary. if key's missing will grab basecolor
-
+
# use these to mark area where the "real" drawing is going on
self.graph_x, self.graph_y = 0, 0
self.graph_width, self.graph_height = None, None
-
+
self.mouse_bar = None
if self.interactive:
self.connect("mouse-over", self.on_mouse_over)
self.connect("button-release", self.on_clicked)
-
+
self.bars_selected = []
-
-
+
+
def on_mouse_over(self, area, region):
if region:
self.mouse_bar = int(region[0])
else:
self.mouse_bar = None
-
+
self.redraw_canvas()
-
+
def on_clicked(self, area, bar):
self.emit("bar-clicked", self.mouse_bar)
-
+
def select_bar(self, index):
pass
@@ -181,15 +181,15 @@ class Chart(graphics.Area):
# returns color darkened by it's index
# the approach reduces contrast by each step
base_color = self.bar_base_color or (220, 220, 220)
-
+
base_hls = colorsys.rgb_to_hls(*base_color)
-
+
step = (base_hls[1] - 30) / 10 #will go from base down to 20 and max 22 steps
-
+
return colorsys.hls_to_rgb(base_hls[0],
base_hls[1] - step * index,
base_hls[2])
-
+
def draw_bar(self, x, y, w, h, color = None):
""" draws a simple bar"""
@@ -219,15 +219,15 @@ class Chart(graphics.Area):
def on_expose(self):
- # fill whole area
+ # fill whole area
if self.background:
self.fill_area(0, 0, self.width, self.height, self.background)
-
+
def _update_targets(self):
# calculates new factors and then updates existing set
max_value = float(self.max_value) or 1 # avoid division by zero
-
+
self.bars = size_list(self.bars, self.data)
#need function to go recursive
@@ -240,12 +240,11 @@ 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
-
+
retarget(self.bars, self.data)
@@ -256,9 +255,9 @@ class Chart(graphics.Area):
self.layout.set_text(label)
label_w, label_h = self.layout.get_pixel_size()
max_extent = max(label_w + 5, max_extent)
-
+
return max_extent
-
+
def draw(self):
logging.error("OMG OMG, not implemented!!!")
@@ -270,7 +269,7 @@ class BarChart(Chart):
if not self.data:
return
- context = self.context
+ context = self.context
context.set_line_width(1)
@@ -282,7 +281,7 @@ class BarChart(Chart):
grid_stride = int(self.max_value * self.grid_stride)
else:
grid_stride = int(self.grid_stride)
-
+
scale_labels = [self.value_format % i
for i in range(grid_stride, int(self.max_value), grid_stride)]
self.legend_width = legend_width = self.legend_width or self.longest_label(scale_labels)
@@ -321,32 +320,49 @@ class BarChart(Chart):
bar_width = min(self.graph_width / float(len(self.keys)), self.max_bar_width)
for i, key in enumerate(self.keys):
exes[key] = (x + self.graph_x, round(bar_width - 1))
-
+
x = x + round(bar_width)
bar_width = min(self.max_bar_width,
(self.graph_width - x) / float(max(1, len(self.keys) - i - 1)))
+ # now for the text - we want reduced contrast for relaxed visuals
+ fg_color = self.get_style().fg[gtk.STATE_NORMAL].to_string()
+ if self.colors.is_light(fg_color):
+ label_color = self.colors.darker(fg_color, 80)
+ else:
+ label_color = self.colors.darker(fg_color, -80)
+
+
for key, bar, data in zip(self.keys, self.bars, self.data):
- self.set_color(graphics.Colors.aluminium[5]);
+ self.set_color(label_color);
self.layout.set_text(key)
label_w, label_h = self.layout.get_pixel_size()
intended_x = exes[key][0] + (exes[key][1] - label_w) / 2
-
+
if not prev_label_end or intended_x > prev_label_end:
self.context.move_to(intended_x, self.graph_height + 4)
context.show_layout(self.layout)
-
+
prev_label_end = intended_x + label_w + 3
-
+
bar_start = 0
- base_color = self.bar_base_color or (220, 220, 220)
-
+
+ # determine bar color
+ base_color = self.bar_base_color
+ if not base_color: #yay, we can be theme friendly!
+ bg_color = self.get_style().bg[gtk.STATE_NORMAL].to_string()
+ if self.colors.is_light(bg_color):
+ base_color = self.colors.darker(bg_color, 30)
+ else:
+ base_color = self.colors.darker(bg_color, -30)
+ tick_color = self.colors.darker(bg_color, -50)
+
if self.stack_keys:
remaining_fractions, remaining_pixels = 1, max_bar_size
-
+
for j, stack_bar in enumerate(bar):
if stack_bar.size > 0:
bar_size = round(remaining_pixels * (stack_bar.size / remaining_fractions))
@@ -354,7 +370,7 @@ class BarChart(Chart):
remaining_pixels -= bar_size
bar_start += bar_size
-
+
last_color = self.stack_key_colors.get(self.stack_keys[j]) or self.get_bar_color(j)
self.draw_bar(exes[key][0],
self.graph_height - bar_start,
@@ -378,30 +394,35 @@ class BarChart(Chart):
total_value = sum(data[i])
else:
total_value = data[i]
-
+
self.layout.set_width(-1)
self.layout.set_text(self.value_format % total_value)
label_w, label_h = self.layout.get_pixel_size()
-
+
if bar_start > label_h + 2:
label_y = self.graph_y + self.graph_height - bar_start + 5
else:
label_y = self.graph_y + self.graph_height - bar_start - label_h + 5
-
+
context.move_to(self.exes[key][0] + (self.exes[key][1] - label_w) / 2.0,
label_y)
# we are in the bar so make sure that the font color is distinguishable
- if colorsys.rgb_to_hls(*graphics.Colors.rgb(last_color))[1] < 150:
- self.set_color(graphics.Colors.almost_white)
+ if self.colors.is_light(last_color):
+ self.set_color(label_color)
else:
- self.set_color(graphics.Colors.aluminium[5])
+ self.set_color(self.colors.almost_white)
context.show_layout(self.layout)
#white grid and scale values
+ if self.background:
+ grid_color = self.background
+ else:
+ grid_color = self.get_style().bg[gtk.STATE_NORMAL].to_string()
+
self.layout.set_width(-1)
if self.grid_stride and self.max_value:
# if grid stride is less than 1 then we consider it to be percentage
@@ -409,7 +430,7 @@ class BarChart(Chart):
grid_stride = int(self.max_value * self.grid_stride)
else:
grid_stride = int(self.grid_stride)
-
+
context.set_line_width(1)
for i in range(grid_stride, int(self.max_value), grid_stride):
y = round(max_bar_size * (i / self.max_value)) + 0.5
@@ -419,10 +440,10 @@ class BarChart(Chart):
label_w, label_h = self.layout.get_pixel_size()
context.move_to(legend_width - label_w - 8,
y - label_h / 2)
- self.set_color(graphics.Colors.aluminium[4])
+ self.set_color(self.colors.aluminium[4])
context.show_layout(self.layout)
- self.set_color("#ffffff")
+ self.set_color(grid_color)
self.context.move_to(legend_width, y)
self.context.line_to(self.width, y)
@@ -430,17 +451,17 @@ class BarChart(Chart):
#stack keys
if self.show_stack_labels:
#put series keys
- self.set_color(graphics.Colors.aluminium[5]);
-
+ self.set_color(label_color);
+
y = self.graph_height
label_y = None
- # if labels are at end, then we need show them for the last bar!
+ # if labels are at end, then we need show them for the last bar!
if self.labels_at_end:
factors = self.bars[-1]
else:
factors = self.bars[0]
-
+
if isinstance(factors, Bar):
factors = [factors]
@@ -450,28 +471,28 @@ class BarChart(Chart):
self.layout.set_alignment(pango.ALIGN_LEFT)
else:
self.layout.set_alignment(pango.ALIGN_RIGHT)
-
+
for j in range(len(factors)):
factor = factors[j].size
bar_size = factor * max_bar_size
-
+
if round(bar_size) > 0 and self.stack_keys:
label = "%s" % self.stack_keys[j]
-
+
self.layout.set_text(label)
label_w, label_h = self.layout.get_pixel_size()
-
+
y -= bar_size
intended_position = round(y + (bar_size - label_h) / 2)
-
+
if label_y:
label_y = min(intended_position, label_y - label_h)
else:
label_y = intended_position
-
+
if self.labels_at_end:
- label_x = self.graph_x + self.graph_width
+ label_x = self.graph_x + self.graph_width
line_x1 = self.graph_x + self.graph_width - 1
line_x2 = self.graph_x + self.graph_width - 6
else:
@@ -499,13 +520,13 @@ class HorizontalBarChart(Chart):
context = self.context
rowcount, keys = len(self.keys), self.keys
-
+
# push graph to the right, so it doesn't overlap
legend_width = self.legend_width or self.longest_label(keys)
-
+
self.graph_x = legend_width
self.graph_x += 8 #add another 8 pixes of padding
-
+
self.graph_width = self.width - self.graph_x
self.graph_y, self.graph_height = 0, self.height
@@ -513,7 +534,7 @@ class HorizontalBarChart(Chart):
if self.chart_background:
self.fill_area(self.graph_x, self.graph_y, self.graph_width, self.graph_height, self.chart_background)
-
+
if not self.data: # go home if we have nothing
return
@@ -522,7 +543,7 @@ class HorizontalBarChart(Chart):
bar_width = min(self.graph_height / float(len(self.keys)), self.max_bar_width)
for i, key in enumerate(self.keys):
positions[key] = (y + self.graph_y, round(bar_width - 1))
-
+
y = y + round(bar_width)
bar_width = min(self.max_bar_width,
(self.graph_height - y) / float(max(1, len(self.keys) - i - 1)))
@@ -532,14 +553,34 @@ class HorizontalBarChart(Chart):
self.layout.set_alignment(pango.ALIGN_RIGHT)
self.layout.set_ellipsize(pango.ELLIPSIZE_END)
-
-
+
+
context.set_line_width(1)
# bars and labels
self.layout.set_width(legend_width * pango.SCALE)
-
+
+
+ # determine bar color
+ base_color = self.bar_base_color
+ if not base_color: #yay, we can be theme friendly!
+ bg_color = self.get_style().bg[gtk.STATE_NORMAL].to_string()
+ if self.colors.is_light(bg_color):
+ base_color = self.colors.darker(bg_color, 30)
+ else:
+ base_color = self.colors.darker(bg_color, -30)
+ tick_color = self.colors.darker(bg_color, -50)
+ last_color = base_color
+
+
+ # now for the text - we want reduced contrast for relaxed visuals
+ fg_color = self.get_style().fg[gtk.STATE_NORMAL].to_string()
+ if self.colors.is_light(fg_color):
+ label_color = self.colors.darker(fg_color, 80)
+ else:
+ label_color = self.colors.darker(fg_color, -80)
+
for i, label in enumerate(keys):
if self.interactive:
@@ -552,20 +593,23 @@ class HorizontalBarChart(Chart):
self.layout.set_width(legend_width * pango.SCALE)
self.layout.set_text(label)
label_w, label_h = self.layout.get_pixel_size()
-
- self.set_color(graphics.Colors.aluminium[5])
- context.move_to(0, positions[label][0] + (positions[label][1] - label_h) / 2)
- context.show_layout(self.layout)
+ label_y = positions[label][0] + (positions[label][1] - label_h) / 2
- base_color = self.bar_base_color or (220, 220, 220)
+ if i == self.mouse_bar:
+ self.set_color(self.get_style().fg[gtk.STATE_PRELIGHT])
+ else:
+ self.set_color(label_color)
+
+
+ context.move_to(0, label_y)
+ context.show_layout(self.layout)
- last_color = (255,255,255)
if self.stack_keys:
bar_start = 0
remaining_fractions, remaining_pixels = 1, max_bar_size
-
+
for j, stack_bar in enumerate(self.bars[i]):
if stack_bar.size > 0:
bar_size = round(remaining_pixels * (stack_bar.size / remaining_fractions))
@@ -586,7 +630,7 @@ class HorizontalBarChart(Chart):
if i in self.bars_selected:
last_color = self.get_style().bg[gtk.STATE_SELECTED].to_string()
elif i == self.mouse_bar:
- last_color = self.get_style().bg[gtk.STATE_PRELIGHT].to_string()
+ last_color = self.get_style().base[gtk.STATE_PRELIGHT].to_string()
else:
last_color = self.key_colors.get(self.keys[i]) or base_color
@@ -596,33 +640,48 @@ class HorizontalBarChart(Chart):
positions[label][1],
last_color)
- # values on bars
+
+ # value labels
if self.stack_keys:
total_value = sum(self.data[i])
else:
total_value = self.data[i]
-
+
self.layout.set_width(-1)
self.layout.set_text(self.value_format % total_value)
label_w, label_h = self.layout.get_pixel_size()
vertical_padding = max((positions[label][1] - label_h) / 2.0, 1)
+
+ label_y = positions[label][0] + (positions[label][1] - label_h) / 2.0
if bar_start - vertical_padding < label_w:
label_x = self.graph_x + bar_start + vertical_padding
- self.set_color(graphics.Colors.aluminium[5])
+
+ # avoid zero selected bars without any hints
+ if not self.stack_keys and i in self.bars_selected and self.bars[i].value == 0:
+ self.set_color(self.get_style().bg[gtk.STATE_SELECTED])
+ self.draw_rect(label_x - 2,
+ label_y - 2,
+ label_w + 4,
+ label_h + 4, 4)
+ self.context.fill()
+ self.set_color(self.get_style().fg[gtk.STATE_SELECTED])
+ else:
+ self.set_color(label_color)
else:
+ label_x = self.graph_x + bar_start - label_w - vertical_padding
+
if i in self.bars_selected:
self.set_color(self.get_style().fg[gtk.STATE_SELECTED].to_string())
else:
# we are in the bar so make sure that the font color is distinguishable
- if colorsys.rgb_to_hls(*graphics.Colors.rgb(last_color))[1] < 150:
- self.set_color(graphics.Colors.almost_white)
+ if self.colors.is_light(last_color):
+ self.set_color(label_color)
else:
- self.set_color(graphics.Colors.aluminium[5])
-
- label_x = self.graph_x + bar_start - label_w - vertical_padding
-
- context.move_to(label_x, positions[label][0] + (positions[label][1] - label_h) / 2.0)
+ self.set_color(self.colors.almost_white)
+
+
+ context.move_to(label_x, label_y)
context.show_layout(self.layout)
context.stroke()
@@ -636,39 +695,39 @@ class HorizontalDayChart(Chart):
def __init__(self, *args, **kwargs):
Chart.__init__(self, *args, **kwargs)
self.start_time, self.end_time = None, None
-
+
def plot_day(self, keys, data, start_time = None, end_time = None):
self.keys, self.data = keys, data
self.start_time, self.end_time = start_time, end_time
self.show()
self.redraw_canvas()
-
+
def on_expose(self):
context = self.context
-
+
Chart.on_expose(self)
rowcount, keys = len(self.keys), self.keys
-
+
start_hour = 0
if self.start_time:
start_hour = self.start_time
- end_hour = 24 * 60
+ end_hour = 24 * 60
if self.end_time:
end_hour = self.end_time
-
-
+
+
# push graph to the right, so it doesn't overlap
legend_width = self.legend_width or self.longest_label(keys)
self.graph_x = legend_width
self.graph_x += 8 #add another 8 pixes of padding
-
+
self.graph_width = self.width - self.graph_x
-
+
#on the botttom leave some space for label
self.layout.set_text("1234567890:")
label_w, label_h = self.layout.get_pixel_size()
-
+
self.graph_y, self.graph_height = 0, self.height - label_h - 4
@@ -678,47 +737,64 @@ class HorizontalDayChart(Chart):
if not self.data: #if we have nothing, let's go home
return
-
+
positions = {}
y = 0
bar_width = min(self.graph_height / float(len(self.keys)), self.max_bar_width)
for i, key in enumerate(self.keys):
positions[key] = (y + self.graph_y, round(bar_width - 1))
-
+
y = y + round(bar_width)
bar_width = min(self.max_bar_width,
(self.graph_height - y) / float(max(1, len(self.keys) - i - 1)))
-
+
max_bar_size = self.graph_width - 15
+
+ # now for the text - we want reduced contrast for relaxed visuals
+ fg_color = self.get_style().fg[gtk.STATE_NORMAL].to_string()
+ if self.colors.is_light(fg_color):
+ label_color = self.colors.darker(fg_color, 80)
+ else:
+ label_color = self.colors.darker(fg_color, -80)
+
+
self.layout.set_alignment(pango.ALIGN_RIGHT)
self.layout.set_ellipsize(pango.ELLIPSIZE_END)
-
+
# bars and labels
self.layout.set_width(legend_width * pango.SCALE)
factor = max_bar_size / float(end_hour - start_hour)
+ # determine bar color
+ base_color = self.bar_base_color
+ if not base_color: #yay, we can be theme friendly!
+ bg_color = self.get_style().bg[gtk.STATE_NORMAL].to_string()
+ if self.colors.is_light(bg_color):
+ base_color = self.colors.darker(bg_color, 30)
+ else:
+ base_color = self.colors.darker(bg_color, -30)
+ tick_color = self.colors.darker(bg_color, -50)
+
for i, label in enumerate(keys):
- self.set_color(graphics.Colors.aluminium[5])
-
+ self.set_color(label_color)
+
self.layout.set_text(label)
label_w, label_h = self.layout.get_pixel_size()
context.move_to(0, positions[label][0] + (positions[label][1] - label_h) / 2)
context.show_layout(self.layout)
- base_color = self.bar_base_color or [220, 220, 220]
-
if isinstance(self.data[i], list) == False:
self.data[i] = [self.data[i]]
-
+
for row in self.data[i]:
bar_x = round((row[0]- start_hour) * factor)
bar_size = round((row[1] - start_hour) * factor - bar_x)
-
+
self.draw_bar(round(self.graph_x + bar_x),
positions[label][0],
bar_size,
@@ -732,9 +808,16 @@ class HorizontalDayChart(Chart):
pace = ((end_hour - start_hour) / 3) / 60 * 60
last_position = positions[keys[-1]]
+
+
+ if self.background:
+ grid_color = self.background
+ else:
+ grid_color = self.get_style().bg[gtk.STATE_NORMAL].to_string()
+
for i in range(start_hour + 60, end_hour, pace):
x = round((i - start_hour) * factor)
-
+
minutes = i % (24 * 60)
self.layout.set_markup(dt.time(minutes / 60, minutes % 60).strftime("%H<small><sup>%M</sup></small>"))
@@ -742,16 +825,16 @@ class HorizontalDayChart(Chart):
context.move_to(self.graph_x + x - label_w / 2,
last_position[0] + last_position[1] + 4)
- self.set_color(graphics.Colors.aluminium[4])
+ self.set_color(self.colors.aluminium[4])
context.show_layout(self.layout)
-
- self.set_color((255, 255, 255))
+
+ self.set_color(grid_color)
self.context.move_to(round(self.graph_x + x) + 0.5, self.graph_y)
self.context.line_to(round(self.graph_x + x) + 0.5,
last_position[0] + last_position[1])
-
+
context.stroke()
@@ -762,12 +845,12 @@ class BasicWindow:
window.set_title("Hamster Charting")
window.set_size_request(400, 300)
window.connect("delete_event", lambda *args: gtk.main_quit())
-
+
self.stacked = BarChart(background = "#fafafa",
bar_base_color = (220, 220, 220),
legend_width = 20,
show_stack_labels = True)
-
+
box = gtk.VBox()
box.pack_start(self.stacked)
@@ -778,7 +861,7 @@ class BasicWindow:
import random
self.data = [[random.randint(0, 10) for j in range(len(self.stacks))] for i in range(len(self.series))]
-
+
color_buttons = gtk.HBox()
color_buttons.set_spacing(4)
@@ -788,13 +871,13 @@ class BasicWindow:
color_buttons.pack_start(button)
box.pack_start(color_buttons, False)
-
+
window.add(box)
window.show_all()
self.plot()
-
-
+
+
def plot(self):
self.stacked.stack_key_colors = self.stack_colors
self.stacked.plot(self.series, self.data, self.stacks)
diff --git a/src/gui/widgets/graphics.py b/src/gui/widgets/graphics.py
index 6897e04..ff9330b 100644
--- a/src/gui/widgets/graphics.py
+++ b/src/gui/widgets/graphics.py
@@ -24,14 +24,16 @@ import pango, cairo
import pytweener
from pytweener import Easing
+import colorsys
class Colors(object):
aluminium = [(238, 238, 236), (211, 215, 207), (186, 189, 182),
(136, 138, 133), (85, 87, 83), (46, 52, 54)]
almost_white = (250, 250, 250)
- @staticmethod
- def color(color):
+ def parse(self, color):
+ assert color is not None
+
#parse color into rgb values
if isinstance(color, str) or isinstance(color, unicode):
color = gtk.gdk.Color(color)
@@ -44,10 +46,19 @@ class Colors(object):
color = [c / 255.0 for c in color]
return color
-
- @staticmethod
- def rgb(color):
- return [c * 255 for c in Colors.color(color)]
+
+ def rgb(self, color):
+ return [c * 255 for c in self.parse(color)]
+
+ def is_light(self, color):
+ # tells you if color is dark or light, so you can up or down the scale for improved contrast
+ return colorsys.rgb_to_hls(*self.rgb(color))[1] > 150
+
+ def darker(self, color, step):
+ # returns color darker by step (where step is in range 0..255)
+ hls = colorsys.rgb_to_hls(*self.rgb(color))
+ return colorsys.hls_to_rgb(hls[0], hls[1] - step, hls[2])
+
class Area(gtk.DrawingArea):
"""Abstraction on top of DrawingArea to work specifically with cairo"""
@@ -55,7 +66,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, gobject.TYPE_PYOBJECT)),
}
def __init__(self):
@@ -66,6 +80,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)
@@ -76,12 +91,16 @@ class Area(gtk.DrawingArea):
self.context, self.layout = None, None
self.width, self.height = None, None
self.__prev_mouse_regions = None
-
+
self.tweener = pytweener.Tweener(0.4, pytweener.Easing.Cubic.easeInOut)
self.framerate = 80
self.last_frame_time = None
self.__animating = False
+ self.mouse_drag = (None, None)
+
+ self.colors = Colors() # handier this way
+
def on_expose(self):
""" on_expose event is where you hook in all your drawing
canvas has been initialized for you """
@@ -93,15 +112,15 @@ class Area(gtk.DrawingArea):
self.__animating = True
self.last_frame_time = dt.datetime.now()
gobject.timeout_add(1000 / self.framerate, self.__interpolate)
-
+
""" animation bits """
def __interpolate(self):
self.__animating = self.tweener.hasTweens()
if not self.window: #will wait until window comes
return self.__animating
-
-
+
+
time_since_start = (dt.datetime.now() - self.last_frame_time).microseconds / 1000000.0
self.tweener.update(time_since_start)
@@ -111,13 +130,15 @@ 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 """
def draw_rect(self, x, y, w, h, corner_radius = 0):
@@ -131,7 +152,7 @@ class Area(gtk.DrawingArea):
x2, y2 = x + w, y + h
half_corner = corner_radius / 2
-
+
self.context.move_to(x + corner_radius, y);
self.context.line_to(x2 - corner_radius, y);
# top-right
@@ -162,7 +183,7 @@ class Area(gtk.DrawingArea):
if color:
self.set_color(color, opacity)
self.context.rectangle(x, y, w, h)
-
+
def fill_area(self, x, y, w, h, color, opacity = 0):
self.rectangle(x, y, w, h, color, opacity)
self.context.fill()
@@ -171,9 +192,9 @@ class Area(gtk.DrawingArea):
# sets text and returns width and height of the layout
self.layout.set_text(text)
return self.layout.get_pixel_size()
-
+
def set_color(self, color, opacity = None):
- color = Colors.color(color) #parse whatever we have there into a normalized triplet
+ color = self.colors.parse(color) #parse whatever we have there into a normalized triplet
if opacity:
self.context.set_source_rgba(color[0], color[1], color[2], opacity)
@@ -204,28 +225,30 @@ class Area(gtk.DrawingArea):
self.layout.set_font_description(default_font)
alloc = self.get_allocation() #x, y, width, height
self.width, self.height = alloc.width, alloc.height
-
+
self.mouse_regions = [] #reset since these can move in each redraw
self.on_expose()
""" 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:
x = event.x
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]:
mouse_regions.append(region[4])
-
+
if mouse_regions:
area.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
else:
@@ -240,14 +263,40 @@ 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
-
+
+ click = False
+ 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
+ click = True
+ self.mouse_drag = None
+
+ if not self.mouse_regions:
+ self.emit("mouse-click", (x,y), [])
+ return
+
mouse_regions = []
for region in self.mouse_regions:
if region[0] < x < region[2] and region[1] < y < region[3]:
@@ -256,7 +305,8 @@ class Area(gtk.DrawingArea):
if mouse_regions:
self.emit("button-release", mouse_regions)
-
+ self.emit("mouse-click", (x,y), mouse_regions)
+
""" simple example """
@@ -267,31 +317,31 @@ class SampleArea(Area):
self.rect_width, self.rect_height = 90, 90
self.text_y = -100
-
-
+
+
def on_expose(self):
# on expose is called when we are ready to draw
-
+
# fill_area is just a shortcut function
# feel free to use self.context. move_to, line_to and others
self.font_size = 32
self.layout.set_text("Hello, World!")
-
+
self.draw_rect(round(self.rect_x),
round(self.rect_y),
self.rect_width,
self.rect_height,
10)
-
+
self.set_color("#ff00ff")
self.context.fill()
self.context.move_to((self.width - self.layout.get_pixel_size()[0]) / 2,
self.text_y)
-
+
self.set_color("#333")
self.context.show_layout(self.layout)
-
+
class BasicWindow:
def __init__(self):
@@ -299,20 +349,20 @@ class BasicWindow:
window.set_title("Graphics Module")
window.set_size_request(300, 300)
window.connect("delete_event", lambda *args: gtk.main_quit())
-
+
self.graphic = SampleArea()
-
+
box = gtk.VBox()
box.pack_start(self.graphic)
-
+
button = gtk.Button("Hello")
button.connect("clicked", self.on_go_clicked)
box.add_with_properties(button, "expand", False)
-
+
window.add(box)
window.show_all()
-
+
# drop the hello on init
self.graphic.animate(self.graphic,
dict(text_y = 120),
@@ -322,13 +372,13 @@ class BasicWindow:
def on_go_clicked(self, widget):
import random
-
+
# set x and y to random position within the drawing area
x = round(min(random.random() * self.graphic.width,
self.graphic.width - self.graphic.rect_width))
y = round(min(random.random() * self.graphic.height,
self.graphic.height - self.graphic.rect_height))
-
+
# here we call the animate function with parameters we would like to change
# the easing functions outside graphics module can be accessed via
# graphics.Easing
@@ -341,4 +391,3 @@ class BasicWindow:
if __name__ == "__main__":
example = BasicWindow()
gtk.main()
-
\ No newline at end of file
diff --git a/src/gui/widgets/pytweener.py b/src/gui/widgets/pytweener.py
index 113b4a5..c8c3876 100644
--- a/src/gui/widgets/pytweener.py
+++ b/src/gui/widgets/pytweener.py
@@ -5,202 +5,150 @@
# Heavily based on caurina Tweener: http://code.google.com/p/tweener/
#
# Released under M.I.T License - see above url
-# Python version by Ben Harling 2009
+# Python version by Ben Harling 2009
+# 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.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
-
-
+
+
def addTween(self, obj, **kwargs):
""" addTween( object, **kwargs) -> tweenObject or False
-
+
Example:
tweener.addTween( myRocket, throttle=50, setThrust=400, tweenTime=5.0, tweenType=tweener.OUT_QUAD )
-
+
You must first specify an object, and at least one property or function with a corresponding
change value. The tween will throw an error if you specify an attribute the object does
not possess. Also the data types of the change and the initial value of the tweened item
must match. If you specify a 'set' -type function, the tweener will attempt to get the
- starting value by call the corresponding 'get' function on the object. If you specify a
- property, the tweener will read the current state as the starting value. You add both
+ starting value by call the corresponding 'get' function on the object. If you specify a
+ property, the tweener will read the current state as the starting value. You add both
functions and property changes to the same tween.
-
+
in addition to any properties you specify on the object, these keywords do additional
setup of the tween.
-
+
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:
t_delay = kwargs.pop("tweenDelay")
else: t_delay = 0
-
+
tw = Tween( obj, t_time, t_type, t_completeFunc, t_updateFunc, t_delay, **kwargs )
- if tw:
- self.currentTweens.append( tw )
+ if tw:
+ tweenlist = self.currentTweens.setdefault(obj, [])
+ tweenlist.append(tw)
return tw
-
+
def removeTween(self, tweenObj):
- if tweenObj in self.currentTweens:
- tweenObj.complete = True
- #self.currentTweens.remove( tweenObj )
-
+ tweenObj.complete = True
+
def getTweensAffectingObject(self, obj):
"""Get a list of all tweens acting on the specified object
Useful for manipulating tweens on the fly"""
- tweens = []
- for t in self.currentTweens:
- if t.target is obj:
- tweens.append(t)
- return tweens
-
- def removeTweeningFrom(self, obj):
+ return self.currentTweens.get(obj, [])
+
+ def killTweensOf(self, obj):
"""Stop tweening an object, without completing the motion
or firing the completeFunction"""
- for t in self.currentTweens:
- if t.target is obj:
- t.complete = True
-
+ try:
+ del self.currentTweens[obj]
+ except:
+ pass
+
+
def finish(self):
#go to last frame for all tweens
- for t in self.currentTweens:
- t.update(t.duration)
- self.currentTweens = []
-
+ for obj in self.currentTweens:
+ for t in self.currentTweens[obj]:
+ t.update(t.duration)
+ self.currentTweens = {}
+
def update(self, timeSinceLastFrame):
- removable = []
- for t in self.currentTweens:
- t.update(timeSinceLastFrame)
-
- if t.complete:
- removable.append(t)
-
- for t in removable:
- self.currentTweens.remove(t)
-
-
+ for obj in self.currentTweens.keys():
+ # updating tweens from last to first and deleting while at it
+ # in order to not confuse the index
+ 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]
+
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
- propName = k
-
- except:
- func = getattr( self.target, k)
- funcName = 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 = [ k, func, tweenable]
-
- #setattr(self, funcName, newFunc[2])
- self.tFuncs.append( newFunc )
-
-
- if prop:
- tweenable = Tweenable( startVal, newVal - startVal)
- newProp = [ k, prop, tweenable]
- self.tProps.append( newProp )
-
-
+
def pause( self, numSeconds=-1 ):
"""Pause this tween
do tween.pause( 2 ) to pause for a specific time
or tween.pause() which pauses indefinitely."""
self.paused = True
self.delay = numSeconds
-
+
def resume( self ):
"""Resume from pause"""
if self.paused:
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
-
+
if self.paused:
if self.delay > 0:
self.delay = max( 0, self.delay - ptime )
@@ -210,65 +158,22 @@ class Tween(object):
if self.updateFunction:
self.updateFunction()
return
-
- self.delta = min(self.delta + ptime, self.duration)
-
-
- for propName, prop, tweenable in self.tProps:
- self.target.__dict__[prop] = self.tween( self.delta, tweenable.startValue, tweenable.change, self.duration )
- for funcName, func, tweenable in self.tFuncs:
- func( self.tween( self.delta, tweenable.startValue, tweenable.change, self.duration ) )
-
-
+
+ self.delta = self.delta + ptime
+ if self.delta > self.duration:
+ self.delta = 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:
self.completeFunction()
-
+
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).
@@ -276,7 +181,7 @@ There certainly is room for improvement, but wanted to keep the readability to s
================================================================================
Easing Equations
- (c) 2003 Robert Penner, all rights reserved.
+ (c) 2003 Robert Penner, all rights reserved.
This work is subject to the terms in
http://www.robertpenner.com/easing_terms_of_use.html.
================================================================================
@@ -310,44 +215,44 @@ 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):
t = t / (d * 0.5)
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):
@@ -361,57 +266,57 @@ class Easing:
return Easing.Bounce.easeOut (t * 2 -d, 0, c, d) * .5 + c*.5 + b
-
- 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
- t = t / d
+ t = t / d
if t == 1: return b+c
-
+
if not p: p = d * .3;
if not a or a < abs(c):
@@ -419,18 +324,18 @@ class Easing:
s = p / 4
else:
s = p / (2 * math.pi) * math.asin(c / a)
-
- t = t - 1
+
+ t = t - 1
return - (a * math.pow(2, 10 * t) * math.sin((t*d-s) * (2 * math.pi) / p)) + b
@staticmethod
def easeOut (t, b, c, d, a = 0, p = 0):
if t == 0: return b
-
+
t = t / d
if (t == 1): return b + c
-
+
if not p: p = d * .3;
if not a or a < abs(c):
@@ -438,17 +343,17 @@ class Easing:
s = p / 4
else:
s = p / (2 * math.pi) * math.asin(c / a)
-
+
return a * math.pow(2,-10 * t) * math.sin((t * d - s) * (2 * math.pi) / p) + c + b
@staticmethod
def easeInOut (t, b, c, d, a = 0, p = 0):
if t == 0: return b
-
+
t = t / (d * 0.5)
if t == 2: return b + c
-
+
if not p: p = d * (.3 * 1.5)
if not a or a < abs(c):
@@ -456,16 +361,16 @@ class Easing:
s = p / 4
else:
s = p / (2 * math.pi) * math.asin(c / a)
-
+
if (t < 1):
t = t - 1
return -.5 * (a * math.pow(2, 10 * t) * math.sin((t * d - s) * (2 * math.pi) / p)) + b
-
+
t = t - 1
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:
@@ -488,14 +393,14 @@ class Easing:
return b+c
t = t / (d * 0.5)
-
+
if t < 1:
return c * 0.5 * math.pow(2, 10 * (t - 1)) + b
-
+
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
@@ -513,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):
@@ -528,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
@@ -589,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
@@ -601,55 +506,52 @@ class Easing:
@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
-
-
-
-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
+ return c * 0.5 * (t * t * t * t * t + 2) + b
+
+
+
+
+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
+
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]