root/gaphas/tags/gaphas-0.3.6/demo.py

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