From: Antonio Ospite Date: Thu, 7 Mar 2013 19:10:51 +0000 (+0100) Subject: Add and example of mouse interaction with cairo and Gtk+ X-Git-Url: https://git.ao2.it/experiments/cairo-gtk.git/commitdiff_plain/7c799435050c02899ffb50299c2a8321650497a1 Add and example of mouse interaction with cairo and Gtk+ --- diff --git a/cairo-gtk-interactivity.py b/cairo-gtk-interactivity.py new file mode 100755 index 0000000..5efa973 --- /dev/null +++ b/cairo-gtk-interactivity.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python + +# An example of mouse interaction with cairo and Gtk+ +# +# Copyright (C) 2013 Antonio Ospite +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# References: +# https://developer.gnome.org/gtk3/3.6/gtk-getting-started.html#idm140520417717488 + +import gi + +gi.require_version('Gtk', '3.0') +from gi.repository import Gdk, Gtk, GObject, Gio + +import math +import colorsys + + +# from http://stackoverflow.com/questions/481144/ +def in_circle(c_x, c_y, r, x, y): + return math.hypot(c_x - x, c_y - y) <= r + + +def clamp(x, interval): + if x < interval[0]: + return interval[0] + elif x > interval[1]: + return interval[1] + else: + return x + + +class InteractiveCanvas(Gtk.DrawingArea): + def __init__(self, width, height, *args, **kwargs): + super(InteractiveCanvas, self).__init__(*args, **kwargs) + + self.width = width + self.height = height + + self.x_offset = self.width / 2.0 + self.y_offset = self.height / 2.0 + self.radius = 20 + self.border = 4 + + self.selectable = False + self.selected = False + self.drag_offset_x = 0 + self.drag_offset_y = 0 + + self.x = self.width / 2.0 + self.y = self.height / 2.0 + + self.set_size_request(width, height) + self.connect('draw', self.on_draw) + self.connect('configure-event', self.on_configure_event) + + self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.POINTER_MOTION_MASK) + self.connect('button-press-event', self.on_button_press_event) + self.connect('button-release-event', self.on_button_release_event) + self.connect('motion-notify-event', self.on_motion_notify_event) + + def on_configure_event(self, widget, event): + self.width = event.width + self.height = event.height + + def on_draw(self, widget, cr): + cr.rectangle(0, 0, self.width, self.height) + cr.set_source_rgb(1.0, 0.5, 0.5) + cr.fill() + + cr.arc(self.x, self.y, self.radius, 0, 2 * math.pi) + + _h = self.x / float(self.width) + if self.selected: + _l = 0.6 + else: + if self.selectable: + _l = 0.5 + else: + _l = 0.2 + _s = self.y / float(self.height) + _r, _g, _b = colorsys.hls_to_rgb(_h, _l, _s) + cr.set_source_rgb(_r, _g, _b) + cr.fill_preserve() + + cr.set_source_rgb(0, 0, 0) + cr.set_line_width(self.border) + cr.stroke() + + def on_button_press_event(self, widget, event): + if self.selectable: + self.selected = True + self.drag_offset_x = self.x - event.x + self.drag_offset_y = self.y - event.y + self.queue_draw() + + def on_button_release_event(self, widget, event): + self.selected = False + self.queue_draw() + + def on_motion_notify_event(self, widget, event): + if self.selected: + self.x = clamp(event.x + self.drag_offset_x, [0, self.width]) + self.y = clamp(event.y + self.drag_offset_y, [0, self.height]) + self.queue_draw() + else: + old_selectable = self.selectable + if in_circle(self.x, self.y, self.radius + self.border, event.x, event.y): + self.selectable = True + else: + self.selectable = False + + if self.selectable != old_selectable: + self.queue_draw() + + +class InteractiveApp(Gtk.Application): + def __init__(self, title, width, height, *args, **kwargs): + super(InteractiveApp, self).__init__(*args, **kwargs) + self.connect("activate", self.on_activate) + self.title = title + self.width = width + self.height = height + + def on_activate(self, data=None): + window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL) + window.set_title(self.title) + + canvas = InteractiveCanvas(self.width, self.height) + window.add(canvas) + window.show_all() + self.add_window(window) + + +if __name__ == "__main__": + app = InteractiveApp( + "InteractiveApp", 640, 480, + application_id="apps.test.interactiveapp", + flags=Gio.ApplicationFlags.FLAGS_NONE) + app.run(None)