| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- #!/usr/bin/env python
- from __future__ import division
- import gtk
- from threading import Thread
- from math import pi, tan
- import src as mt
- from utils import BoundingBoxArea
- RED = 1, 0, 0
- GREEN = 0, 1, 0
- BLUE = 0, 0, 1
- WHITE = 1, 1, 1
- BLACK = 0, 0, 0
- class Rectangle(mt.RectangularArea):
- def __init__(self, x, y, width, height, color=(1, 0, 0)):
- super(Rectangle, self).__init__(x, y, width, height)
- self.color = color
- self.on_drag(self.handle_drag)
- def handle_drag(self, g):
- tx, ty = g.get_translation()
- self.translate(tx, ty)
- refresh()
- def draw(self, cr):
- cr.rectangle(self.x, self.y, self.width, self.height)
- cr.set_source_rgb(*self.color)
- cr.fill()
- class Polygon(BoundingBoxArea):
- def __init__(self, x, y, points, color=BLUE, border_color=RED):
- super(Polygon, self).__init__(x, y, points)
- self.fill_color = color
- self.border_color = border_color
- self.on_drag(self.handle_drag)
- self.on_pinch(self.handle_pinch)
- self.on_rotate(self.handle_rotate)
- def handle_drag(self, g):
- tx, ty = g.get_translation()
- self.translate(tx, ty)
- refresh()
- def handle_pinch(self, g):
- cx, cy = g.get_position()
- self.scale_points(g.get_scale(), cx, cy)
- self.update_bounds()
- refresh()
- def handle_rotate(self, g):
- cx, cy = g.get_position()
- self.rotate_points(g.get_angle(), cx, cy)
- self.update_bounds()
- refresh()
- def draw(self, cr):
- # Draw bounding box
- if draw_bounding_boxes:
- cr.rectangle(self.x, self.y, self.width, self.height)
- cr.set_source_rgb(*self.border_color)
- cr.set_line_width(3)
- cr.stroke()
- # Fill polygon
- cr.translate(self.x, self.y)
- cr.new_path()
- for x, y in zip(*self.points):
- cr.line_to(x, y)
- cr.set_source_rgb(*self.fill_color)
- cr.fill()
- fullscreen = False
- draw_bounding_boxes = draw_touch_points = True
- W, H = mt.screen.screen_size
- def create_context_window(w, h, callback):
- def create_context(area, event):
- """Add Cairo context to GTK window and draw state."""
- global cr
- cr = area.window.cairo_create()
- draw()
- def move_window(win, event):
- """Synchronize root multi-touch area with GTK window."""
- root.set_position(*event.get_coords())
- root.set_size(event.width, event.height)
- overlay.set_size(event.width, event.height)
- draw()
- def handle_key(win, event):
- """Handle key event. 'f' toggles fullscreen, 'q' exits the program, 'b'
- toggles bounding boxes, 'p' toggles touch points."""
- if event.keyval >= 256:
- return
- key = chr(event.keyval)
- if key == 'f':
- global fullscreen
- (win.unfullscreen if fullscreen else win.fullscreen)()
- fullscreen = not fullscreen
- elif key == 'q':
- quit()
- elif key == 'b':
- global draw_bounding_boxes
- draw_bounding_boxes = not draw_bounding_boxes
- refresh()
- elif key == 'p':
- global draw_touch_points
- draw_touch_points = not draw_touch_points
- refresh()
- # Root area (will be synchronized with GTK window)
- global root, overlay
- root = mt.RectangularArea(0, 0, w, h)
- overlay = mt.RectangularArea(0, 0, w, h)
- # GTK window
- global window
- window = gtk.Window()
- window.set_title('Cairo test')
- window.connect('destroy', quit)
- window.connect('key-press-event', handle_key)
- window.connect('configure-event', move_window)
- window.connect('show', callback)
- if fullscreen:
- window.fullscreen()
- # Drawing area, needed by cairo context for drawing
- area = gtk.DrawingArea()
- area.set_size_request(w, h)
- area.connect('expose-event', create_context)
- window.add(area)
- area.show()
- window.show()
- def draw(*args):
- if not cr:
- return
- # Background
- cr.rectangle(0, 0, *root.get_size())
- cr.set_source_rgb(*BLACK)
- cr.fill()
- # Drawable objects (use save and restore to allow transformations)
- for obj in draw_objects:
- cr.save()
- obj.draw(cr)
- cr.restore()
- if draw_touch_points:
- ox, oy = root.get_position()
- cr.set_source_rgb(*WHITE)
- for x, y in touch_points.itervalues():
- x -= ox
- y -= oy
- cr.set_line_width(3)
- cr.arc(x, y, 20, 0, 2 * pi)
- cr.stroke()
- cr.set_line_width(1)
- cr.move_to(x - 8, y)
- cr.line_to(x + 8, y)
- cr.move_to(x, y - 8)
- cr.line_to(x, y + 8)
- cr.stroke()
- def refresh():
- window.queue_draw()
- def quit(*args):
- gtk.main_quit()
- # Initialization
- window = cr = root = overlay = None
- draw_objects = []
- touch_points = {}
- def triangle_height(width):
- return abs(.5 * width * tan(2 / 3 * pi))
- def on_show(window):
- def root_dtap(g): print 'double tapped on root'
- root.on_double_tap(root_dtap)
- # Create blue rectangle
- x, y, w, h = 0, 0, 250, 150
- rect = Polygon(x, y, [(0, 0), (0, h), (w, h), (w, 0)])
- draw_objects.append(rect)
- root.add_area(rect)
- def rect_tap(g): print 'tapped on rectangle'
- rect.on_tap(rect_tap, propagate_up_event=False)
- # Create green triangle
- x, y, w = 400, 400, 200
- h = triangle_height(w)
- triangle = Polygon(x, y, [(0, h), (w, h), (w / 2, 0)], color=GREEN)
- draw_objects.append(triangle)
- root.add_area(triangle)
- # Overlay catches basic events
- def handle_down(gesture):
- point = gesture.get_event().get_touch_object()
- touch_points[point.get_id()] = point.get_position()
- if draw_touch_points:
- refresh()
- def handle_up(gesture):
- point = gesture.get_event().get_touch_object()
- del touch_points[point.get_id()]
- if draw_touch_points:
- refresh()
- overlay.on_point_down(handle_down)
- overlay.on_point_move(handle_down)
- overlay.on_point_up(handle_up)
- root.add_area(overlay)
- if __name__ == '__main__':
- from parse_arguments import create_parser, parse_args
- # Parse arguments
- parser = create_parser()
- parser.add_argument('-f', '--fullscreen', action='store_true', default=False,
- help='run in fullscreen initially')
- args = parse_args(parser)
- fullscreen = args.fullscreen
- # Create a window with a Cairo context in it and a multi-touch area
- # syncronized with it
- create_context_window(640, 460, on_show)
- # Run multi-touch gesture server in separate thread
- driver = mt.create_driver(root)
- mt_thread = Thread(target=driver.start)
- mt_thread.daemon = True
- mt_thread.start()
- # Initialize threads in GTK so that the thread started above will work
- gtk.gdk.threads_init()
- # Start main loop in current thread
- gtk.main()
|