cairo-gtk-animation.py: add a more efficient animation strategy
[experiments/cairo-gtk.git] / cairo-gtk-animation.py
1 #!/usr/bin/env python
2
3 # An example of an animation done with cairo and Gtk+
4 #
5 # Copyright (C) 2013  Antonio Ospite <ospite@studenti.unina.it>
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 # References:
21 # http://wiki.laptop.org/go/PyGTK/Smooth_Animation_with_PyGTK
22
23 import gi
24
25 gi.require_version('Gtk', '3.0')
26 from gi.repository import Gtk, GObject, Gio
27 import time
28
29 import math
30 import colorsys
31
32
33 class AnimatedCanvas(Gtk.DrawingArea):
34     def __init__(self,  width, height, *args, **kwargs):
35         super(AnimatedCanvas, self).__init__(*args, **kwargs)
36
37         self.width = width
38         self.height = height
39
40         self.x_offset = 2
41         self.y_offset = 3
42         self.radius = 10
43         self.border = 4
44
45         self.x = self.radius + self.border
46         self.y = self.radius + self.border
47
48         self.set_size_request(width, height)
49         self.connect('draw', self.do_draw_cb)
50         self.connect('configure-event', self.do_configure_event_cb)
51         self.connect('destroy', self.on_destroy)
52
53         self.fps = 30
54         GObject.timeout_add(50, self.mainloop)
55
56     def on_destroy(self, event):
57         self.running = False
58
59     def tick(self):
60         self.update()
61         self.queue_draw()
62
63     def mainloop(self):
64         self.running = True
65         while self.running:
66             time.sleep(1.0 / self.fps)
67             self.tick()
68             while Gtk.events_pending():
69                 Gtk.main_iteration()
70
71         return False
72
73     def update(self):
74         if self.x - self.radius - self.border < 0 or \
75            self.x + self.radius + self.border > self.width:
76             self.x_offset *= -1
77
78         if self.y - self.radius - self.border < 0 or \
79            self.y + self.radius + self.border > self.height:
80             self.y_offset *= -1
81
82         self.x += self.x_offset
83         self.y += self.y_offset
84
85     def do_configure_event_cb(self, widget, event):
86         self.width = event.width
87         self.height = event.height
88
89     def do_draw_cb(self, widget, cr):
90
91         cr.rectangle(0, 0, self.width, self.height)
92         cr.set_source_rgb(1.0, 0.5, 0.5)
93         cr.fill()
94
95         cr.arc(self.x, self.y, self.radius, 0, 2 * math.pi)
96
97         cr.set_line_width(self.border)
98
99         _h = self.x / float(self.width)
100         _l = 0.5
101         _s = self.y / float(self.height)
102         _r, _g, _b = colorsys.hls_to_rgb(_h, _l, _s)
103         cr.set_source_rgb(_r, _g, _b)
104         cr.fill_preserve()
105
106         cr.set_source_rgb(0, 0, 0)
107         cr.stroke()
108
109
110 class AnimatedApp(Gtk.Application):
111     def __init__(self, title, width, height, *args, **kwargs):
112         super(AnimatedApp, self).__init__(*args, **kwargs)
113         self.connect("activate", self.on_activate)
114         self.title = title
115         self.width = width
116         self.height = height
117
118     def on_activate(self, data=None):
119         window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL)
120         window.set_title(self.title)
121
122         canvas = AnimatedCanvas(self.width, self.height)
123         window.add(canvas)
124         window.show_all()
125         self.add_window(window)
126
127
128 if __name__ == "__main__":
129     app = AnimatedApp(
130         "AnimatedApp", 640, 480,
131         application_id="apps.test.animatedapp",
132         flags=Gio.ApplicationFlags.FLAGS_NONE)
133     app.run(None)