root/gaphas/branches/hw/demo.py

Revision 1661, 9.3 kB (checked in by wrobe..@pld-linux.org, 1 year ago)

- synchronized with demo.py on trunk

  • Property svn:executable set to *
  • Property svn:keywords set to Revision HeadURL
Line 
1 #!/usr/bin/env python
2 """
3 A simple demo app.
4
5 It sports a small canvas and some trivial operations:
6
7  - Add a line/box
8  - Zoom in/out
9  - Split a line segment
10  - Delete focused item
11  - Record state changes
12  - Play back state changes (= undo !) With visual updates
13  - Exports to SVG and PNG
14
15 """
16
17 __version__ = "$Revision$"
18 # $HeadURL$
19
20 import pygtk
21 pygtk.require('2.0') 
22
23 import math
24 import gtk
25 import cairo
26 from gaphas import Canvas, GtkView, View
27 from gaphas.examples import Box, Text, FatLine, Circle, DefaultExampleTool
28 from gaphas.item import Line, NW, SE
29 from gaphas.tool import PlacementTool, HandleTool
30 from gaphas.painter import ItemPainter
31 from gaphas import state
32 from gaphas.util import text_extents
33
34 from gaphas import painter
35 #painter.DEBUG_DRAW_BOUNDING_BOX = True
36
37 # Global undo list
38 undo_list = []
39
40 def undo_handler(event):
41     global undo_list
42     undo_list.append(event)
43
44
45 class MyBox(Box):
46     """Box with an example connection protocol.
47     """
48
49 class MyLine(Line):
50     """Line with experimental connection protocol.
51     """
52     def __init__(self):
53         super(MyLine, self).__init__()
54         self.fuzziness = 2
55
56     def draw_head(self, context):
57         cr = context.cairo
58         cr.move_to(0, 0)
59         cr.line_to(10, 10)
60         cr.stroke()
61         # Start point for the line to the next handle
62         cr.move_to(0, 0)
63
64     def draw_tail(self, context):
65         cr = context.cairo
66         cr.line_to(0, 0)
67         cr.line_to(10, 10)
68         cr.stroke()
69
70
71
72
73 class MyText(Text):
74     """
75     Text with experimental connection protocol.
76     """
77    
78     def draw(self, context):
79         Text.draw(self, context)
80         cr = context.cairo
81         w, h = text_extents(cr, self.text, multiline=self.multiline)
82         cr.rectangle(0, 0, w, h)
83         cr.set_source_rgba(.3, .3, 1., .6)
84         cr.stroke()
85
86
87 def create_window(canvas, title, zoom=1.0):
88     view = GtkView()
89     view.tool = DefaultExampleTool()
90
91     w = gtk.Window()
92     w.set_title(title)
93     h = gtk.HBox()
94     w.add(h)
95
96     # VBox contains buttons that can be used to manipulate the canvas:
97     v = gtk.VBox()
98     v.set_property('border-width', 3)
99     v.set_property('spacing', 2)
100     f = gtk.Frame()
101     f.set_property('border-width', 1)
102     f.add(v)
103     h.pack_start(f, expand=False)
104
105     v.add(gtk.Label('Item placement:'))
106    
107     b = gtk.Button('Add box')
108
109     def on_clicked(button, view):
110         #view.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CROSSHAIR))
111         view.tool.grab(PlacementTool(MyBox, HandleTool(), 2))
112
113     b.connect('clicked', on_clicked, view)
114     v.add(b)
115
116     b = gtk.Button('Add line')
117
118     def on_clicked(button):
119         view.tool.grab(PlacementTool(MyLine, HandleTool(), 1))
120
121     b.connect('clicked', on_clicked)
122     v.add(b)
123
124     v.add(gtk.Label('Zooming:'))
125    
126     b = gtk.Button('Zoom in')
127
128     def on_clicked(button):
129         view.zoom(1.2)
130
131     b.connect('clicked', on_clicked)
132     v.add(b)
133
134     b = gtk.Button('Zoom out')
135
136     def on_clicked(button):
137         view.zoom(1/1.2)
138
139     b.connect('clicked', on_clicked)
140     v.add(b)
141
142     v.add(gtk.Label('Misc:'))
143
144     b = gtk.Button('Split line')
145
146     def on_clicked(button):
147         if isinstance(view.focused_item, Line):
148             view.focused_item.split_segment(0)
149             view.queue_draw_item(view.focused_item, handles=True)
150
151     b.connect('clicked', on_clicked)
152     v.add(b)
153
154     b = gtk.Button('Delete focused')
155
156     def on_clicked(button):
157         if view.focused_item:
158             canvas.remove(view.focused_item)
159             print 'items:', canvas.get_all_items()
160
161     b.connect('clicked', on_clicked)
162     v.add(b)
163
164     v.add(gtk.Label('State:'))
165     b = gtk.ToggleButton('Record')
166
167     def on_toggled(button):
168         global undo_list
169         if button.get_active():
170             print 'start recording'
171             del undo_list[:]
172             state.subscribers.add(undo_handler)
173         else:
174             print 'stop recording'
175             state.subscribers.remove(undo_handler)
176
177     b.connect('toggled', on_toggled)
178     v.add(b)
179
180     b = gtk.Button('Play back')
181    
182     def on_clicked(self):
183         global undo_list
184         apply_me = list(undo_list)
185         del undo_list[:]
186         apply_me.reverse()
187         saveapply = state.saveapply
188         for event in apply_me:
189             saveapply(*event)
190             # Visualize each event:
191             while gtk.events_pending():
192                 gtk.main_iteration()
193
194     b.connect('clicked', on_clicked)
195     v.add(b)
196
197     v.add(gtk.Label('Export:'))
198
199     b = gtk.Button('Write demo.png')
200
201     def on_clicked(button):
202         svgview = View(view.canvas)
203         svgview.painter = ItemPainter()
204
205         # Update bounding boxes with a temporaly CairoContext
206         # (used for stuff like calculating font metrics)
207         tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)
208         tmpcr = cairo.Context(tmpsurface)
209         svgview.update_bounding_box(tmpcr)
210         tmpcr.show_page()
211         tmpsurface.flush()
212        
213         w, h = svgview.bounding_box.width, svgview.bounding_box.height
214         surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(w), int(h))
215         cr = cairo.Context(surface)
216         svgview.matrix.translate(-svgview.bounding_box.x0, -svgview.bounding_box.y0)
217         cr.save()
218         svgview.paint(cr)
219
220         cr.restore()
221         cr.show_page()
222         surface.write_to_png('demo.png')
223
224     b.connect('clicked', on_clicked)
225     v.add(b)
226
227     b = gtk.Button('Write demo.svg')
228
229     def on_clicked(button):
230         svgview = View(view.canvas)
231         svgview.painter = ItemPainter()
232
233         # Update bounding boxes with a temporaly CairoContext
234         # (used for stuff like calculating font metrics)
235         tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)
236         tmpcr = cairo.Context(tmpsurface)
237         svgview.update_bounding_box(tmpcr)
238         tmpcr.show_page()
239         tmpsurface.flush()
240        
241         w, h = svgview.bounding_box.width, svgview.bounding_box.height
242         surface = cairo.SVGSurface('demo.svg', w, h)
243         cr = cairo.Context(surface)
244         svgview.matrix.translate(-svgview.bounding_box.x0, -svgview.bounding_box.y0)
245         svgview.paint(cr)
246         cr.show_page()
247         surface.flush()
248         surface.finish()
249
250     b.connect('clicked', on_clicked)
251     v.add(b)
252
253    
254 #    b = gtk.Button('Cursor')
255 #
256 #    def on_clicked(button, li):
257 #        c = li[0]
258 #        li[0] = (c+2) % 154
259 #        button.set_label('Cursor %d' % c)
260 #        button.window.set_cursor(gtk.gdk.Cursor(c))
261 #
262 #    b.connect('clicked', on_clicked, [0])
263 #    v.add(b)
264
265     # Add the actual View:
266
267     t = gtk.Table(2,2)
268     h.add(t)
269
270     w.connect('destroy', gtk.main_quit)
271
272     view.canvas = canvas
273     view.zoom(zoom)
274     view.set_size_request(150, 120)
275     hs = gtk.HScrollbar(view.hadjustment)
276     vs = gtk.VScrollbar(view.vadjustment)
277     t.attach(view, 0, 1, 0, 1)
278     t.attach(hs, 0, 1, 1, 2, xoptions=gtk.FILL, yoptions=gtk.FILL)
279     t.attach(vs, 1, 2, 0, 1, xoptions=gtk.FILL, yoptions=gtk.FILL)
280
281     w.show_all()
282    
283     def handle_changed(view, item, what):
284         print what, 'changed: ', item
285
286     view.connect('focus-changed', handle_changed, 'focus')
287     view.connect('hover-changed', handle_changed, 'hover')
288     view.connect('selection-changed', handle_changed, 'selection')
289
290 def main():
291     c=Canvas()
292
293     create_window(c, 'View created before')
294
295     # Add stuff to the canvas:
296
297     b=MyBox()
298     b.min_width = 20
299     b.min_height = 30
300     print 'box', b
301     b.matrix=(1.0, 0.0, 0.0, 1, 20,20)
302     b.width = b.height = 40
303     c.add(b)
304
305     bb=Box()
306     print 'box', bb
307     bb.matrix=(1.0, 0.0, 0.0, 1, 10,10)
308     c.add(bb, parent=b)
309
310     fl = FatLine()
311     fl.height = 50
312     fl.matrix.translate(100, 100)
313     c.add(fl)
314
315
316     circle = Circle()
317     h1, h2 = circle.handles()
318     circle.radius = 20
319     circle.matrix.translate(50, 100)
320     c.add(circle)
321
322     # AJM: extra boxes:
323     bb=Box()
324     print 'box', bb
325     bb.matrix.rotate(math.pi/4.)
326     c.add(bb, parent=b)
327 #    for i in xrange(10):
328 #        bb=Box()
329 #        print 'box', bb
330 #        bb.matrix.rotate(math.pi/4.0 * i / 10.0)
331 #        c.add(bb, parent=b)
332
333     t=MyText('Single line')
334     t.matrix.translate(70,70)
335     c.add(t)
336
337     l=MyLine()
338     l.handles()[1].pos = (30, 30)
339     l.split_segment(0, 3)
340     l.matrix.translate(30, 60)
341     c.add(l)
342     l.orthogonal = True
343
344     off_y = 0
345     for align_x in (-1, 0, 1):
346         for align_y in (-1, 0, 1):
347             t=MyText('Aligned text %d/%d' % (align_x, align_y),
348                      align_x=align_x, align_y=align_y)
349             t.matrix.translate(120, 200 + off_y)
350             off_y += 30
351             c.add(t)
352
353     t=MyText('Multiple\nlines', multiline = True)
354     t.matrix.translate(70,100)
355     c.add(t)
356
357     ##
358     ## State handling (a.k.a. undo handlers)
359     ##
360
361     # First, activate the revert handler:
362     state.observers.add(state.revert_handler)
363
364     def print_handler(event):
365         print 'event:', event
366
367     #state.subscribers.add(print_handler)
368
369     ##
370     ## Start the main application
371     ##
372
373     create_window(c, 'View created after')
374
375     gtk.main()
376
377 if __name__ == '__main__':
378     import sys
379     if '-p' in sys.argv:
380         print 'Profiling...'
381         import hotshot, hotshot.stats
382         prof = hotshot.Profile('demo-gaphas.prof')
383         prof.runcall(main)
384         prof.close()
385         stats = hotshot.stats.load('demo-gaphas.prof')
386         stats.strip_dirs()
387         stats.sort_stats('time', 'calls')
388         stats.print_stats(20)
389     else:
390         main()
391 # vim: sw=4:et:
Note: See TracBrowser for help on using the browser.