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

Revision 1850, 9.4 kB (checked in by arj..@yirdis.nl, 1 year ago)
  • make image exports work again.
  • View.bounding_box returns qtree soft bounds
  • 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             saveapply(*event)
201             # Visualize each event:
202             while gtk.events_pending():
203                 gtk.main_iteration()
204
205     b.connect('clicked', on_clicked)
206     v.add(b)
207
208     v.add(gtk.Label('Export:'))
209
210     b = gtk.Button('Write demo.png')
211
212     def on_clicked(button):
213         svgview = View(view.canvas)
214         svgview.painter = ItemPainter()
215
216         # Update bounding boxes with a temporaly CairoContext
217         # (used for stuff like calculating font metrics)
218         tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)
219         tmpcr = cairo.Context(tmpsurface)
220         svgview.update_bounding_box(tmpcr)
221         tmpcr.show_page()
222         tmpsurface.flush()
223        
224         w, h = svgview.bounding_box.width, svgview.bounding_box.height
225         surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(w), int(h))
226         cr = cairo.Context(surface)
227         svgview.matrix.translate(-svgview.bounding_box.x, -svgview.bounding_box.y)
228         cr.save()
229         svgview.paint(cr)
230
231         cr.restore()
232         cr.show_page()
233         surface.write_to_png('demo.png')
234
235     b.connect('clicked', on_clicked)
236     v.add(b)
237
238     b = gtk.Button('Write demo.svg')
239
240     def on_clicked(button):
241         svgview = View(view.canvas)
242         svgview.painter = ItemPainter()
243
244         # Update bounding boxes with a temporaly CairoContext
245         # (used for stuff like calculating font metrics)
246         tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)
247         tmpcr = cairo.Context(tmpsurface)
248         svgview.update_bounding_box(tmpcr)
249         tmpcr.show_page()
250         tmpsurface.flush()
251        
252         w, h = svgview.bounding_box.width, svgview.bounding_box.height
253         surface = cairo.SVGSurface('demo.svg', w, h)
254         cr = cairo.Context(surface)
255         svgview.matrix.translate(-svgview.bounding_box.x, -svgview.bounding_box.y)
256         svgview.paint(cr)
257         cr.show_page()
258         surface.flush()
259         surface.finish()
260
261     b.connect('clicked', on_clicked)
262     v.add(b)
263
264    
265     b = gtk.Button('Dump QTree')
266
267     def on_clicked(button, li):
268         view._qtree.dump()
269
270     b.connect('clicked', on_clicked, [0])
271     v.add(b)
272
273     # Add the actual View:
274
275     t = gtk.Table(2,2)
276     h.add(t)
277
278     w.connect('destroy', gtk.main_quit)
279
280     view.canvas = canvas
281     view.zoom(zoom)
282     view.set_size_request(150, 120)
283     hs = gtk.HScrollbar(view.hadjustment)
284     vs = gtk.VScrollbar(view.vadjustment)
285     t.attach(view, 0, 1, 0, 1)
286     t.attach(hs, 0, 1, 1, 2, xoptions=gtk.FILL, yoptions=gtk.FILL)
287     t.attach(vs, 1, 2, 0, 1, xoptions=gtk.FILL, yoptions=gtk.FILL)
288
289     w.show_all()
290    
291     def handle_changed(view, item, what):
292         print what, 'changed: ', item
293
294     view.connect('focus-changed', handle_changed, 'focus')
295     view.connect('hover-changed', handle_changed, 'hover')
296     view.connect('selection-changed', handle_changed, 'selection')
297
298 def main():
299     c=Canvas()
300
301     create_window(c, 'View created before')
302
303     # Add stuff to the canvas:
304
305     b=MyBox()
306     b.min_width = 20
307     b.min_height = 30
308     print 'box', b
309     b.matrix=(1.0, 0.0, 0.0, 1, 20,20)
310     b.width = b.height = 40
311     c.add(b)
312
313     bb=Box()
314     print 'box', bb
315     bb.matrix=(1.0, 0.0, 0.0, 1, 10,10)
316     c.add(bb, parent=b)
317
318     fl = FatLine()
319     fl.height = 50
320     fl.matrix.translate(100, 100)
321     c.add(fl)
322
323
324     circle = Circle()
325     h1, h2 = circle.handles()
326     circle.radius = 20
327     circle.matrix.translate(50, 100)
328     c.add(circle)
329
330     # AJM: extra boxes:
331     bb=Box()
332     print 'box', bb
333     bb.matrix.rotate(math.pi/4.)
334     c.add(bb, parent=b)
335 #    for i in xrange(10):
336 #        bb=Box()
337 #        print 'box', bb
338 #        bb.matrix.rotate(math.pi/4.0 * i / 10.0)
339 #        c.add(bb, parent=b)
340
341     t=MyText('Single line')
342     t.matrix.translate(70,70)
343     c.add(t)
344
345     l=MyLine()
346     l.handles()[1].pos = (30, 30)
347     l.split_segment(0, 3)
348     l.matrix.translate(30, 60)
349     c.add(l)
350     l.orthogonal = True
351
352     off_y = 0
353     for align_x in (-1, 0, 1):
354         for align_y in (-1, 0, 1):
355             t=MyText('Aligned text %d/%d' % (align_x, align_y),
356                      align_x=align_x, align_y=align_y)
357             t.matrix.translate(120, 200 + off_y)
358             off_y += 30
359             c.add(t)
360
361     t=MyText('Multiple\nlines', multiline = True)
362     t.matrix.translate(70,100)
363     c.add(t)
364
365     ##
366     ## State handling (a.k.a. undo handlers)
367     ##
368
369     # First, activate the revert handler:
370     state.observers.add(state.revert_handler)
371
372     def print_handler(event):
373         print 'event:', event
374
375     #state.subscribers.add(print_handler)
376
377     ##
378     ## Start the main application
379     ##
380
381     create_window(c, 'View created after')
382
383     gtk.main()
384
385 if __name__ == '__main__':
386     import sys
387     if '-p' in sys.argv:
388         print 'Profiling...'
389         import hotshot, hotshot.stats
390         prof = hotshot.Profile('demo-gaphas.prof')
391         prof.runcall(main)
392         prof.close()
393         stats = hotshot.stats.load('demo-gaphas.prof')
394         stats.strip_dirs()
395         stats.sort_stats('time', 'calls')
396         stats.print_stats(20)
397     else:
398         main()
399
400 # vim: sw=4:et:
Note: See TracBrowser for help on using the browser.