From 362a5439b10c14766eb05cd31856cba0d36ee395 Mon Sep 17 00:00:00 2001 From: Manuel Quiñones Date: Mon, 26 Mar 2012 05:43:48 +0000 Subject: Code split Toolbar and Drawing separated. Signed-off-by: Manuel Quiñones --- diff --git a/drawing.py b/drawing.py new file mode 100644 index 0000000..2850f4e --- /dev/null +++ b/drawing.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2012 Manuel Quiñones +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from gettext import gettext as _ +import logging + +import gtk +from gtk import gdk +import cairo + + +# This prevents the expose callback to draw a large stroke each time: +STROKE_MAX_POINTS = 80 + + +class Drawing(gtk.DrawingArea): + def __init__(self, parent): + super(Drawing, self).__init__() + + # Communication with the activity, for sharing: + self._parent = parent + + # This are temporal points stored while the stroke is being + # made. When the stroke is finished (release event) the + # stroke is applied to the canvas. + self._stroke_points = [] + + # Our drawing canvas: + self._drawing_canvas = None + + # The settings of our brush: + self._settings = { + 'stroke color': (1.0, 0.0, 0.0, 0.3), + 'stroke width': 8, + } + + # Sharing? + self.we_are_sharing = False + + # The masks to capture the events we are interested in: + self.add_events(gdk.EXPOSURE_MASK | gdk.VISIBILITY_NOTIFY_MASK) + self.add_events(gtk.gdk.BUTTON_PRESS_MASK | \ + gtk.gdk.BUTTON_RELEASE_MASK | \ + gtk.gdk.BUTTON1_MOTION_MASK) + + # Connect the callbacks: + self.connect("expose-event", self._expose_cb) + self.connect("button-press-event", self._press_cb) + self.connect("motion-notify-event", self._motion_cb) + self.connect("button-release-event", self._release_cb) + + def setup(self, width, height): + """Setup a blank canvas of specified size.""" + logging.debug("drawing set up") + self._drawing_canvas = cairo.ImageSurface(cairo.FORMAT_ARGB32, + width, height) + context = cairo.Context(self._drawing_canvas) + context.rectangle(0, 0, width, height) + context.set_source_rgb(1.0, 1.0, 1.0) + context.fill() + self.queue_draw() + + def set_sharing(self, share=True): + """Set sharing True or False.""" + self.we_are_sharing = share + + def get_size(self): + """Return the size of the current canvas.""" + width = self._drawing_canvas.get_width() + height = self._drawing_canvas.get_height() + return width, height + + def remote_stroke(self, stroke_points): + """Draw stroke from other player.""" + context = cairo.Context(self._drawing_canvas) + self._set_stroke_context(context) + self._paint_stroke(context, stroke_points) + self.queue_draw() + + def clear_drawing_canvas(self): + width, height = self.get_size() + self.setup(width, height) + if self.we_are_sharing: + self._parent.send_new_drawing() + + def _set_stroke_context(self, context): + """Set the settings of our brush to the Cairo context.""" + context.set_source_rgba(*self._settings['stroke color']) + context.set_line_width(self._settings['stroke width']) + + def _paint_stroke(self, context, stroke_points): + """Draw lines from the list of points to the Cairo context""" + point_x, point_y = stroke_points[0] + context.move_to(point_x, point_y) + + for point_x, point_y in stroke_points[1:]: + context.line_to(point_x, point_y) + + context.stroke() + + def _send_stroke_to_drawing_canvas(self, continue_stroke=False): + """Paint current stroke in the canvas, clean stroke points.""" + + # Return if there is no stroke to paint: + if self._stroke_points == []: + return + + # Get context from canvas and paint: + context = cairo.Context(self._drawing_canvas) + self._set_stroke_context(context) + self._paint_stroke(context, self._stroke_points) + + if self.we_are_sharing: + self._parent.send_stroke(self._stroke_points) + + # Clean the list of points: + if continue_stroke: + self._stroke_points = self._stroke_points[-1:] + else: + self._stroke_points = [] + + def _press_cb(self, widget, event): + mouse_x, mouse_y, state = event.window.get_pointer() + + # We paint pressing the BUTTON1, return otherwise: + if not state and gtk.gdk.BUTTON1_MASK: + return + + self._stroke_points.append((mouse_x, mouse_y)) + + def _motion_cb(self, widget, event): + if event.is_hint: + mouse_x, mouse_y, state = event.window.get_pointer() + else: + mouse_x = event.x + mouse_y = event.y + state = event.state + + # We paint pressing the BUTTON1, return otherwise: + if not state and gtk.gdk.BUTTON1_MASK: + return + + # Paint stroke in canvas if it gets too big: + if len(self._stroke_points) > STROKE_MAX_POINTS: + self._send_stroke_to_drawing_canvas(continue_stroke=True) + + self._stroke_points.append((mouse_x, mouse_y)) + self.queue_draw() + + def _release_cb(self, widget, event): + self._send_stroke_to_drawing_canvas() + + def _expose_cb(self, widget, event): + context = self.window.cairo_create() + + # Paint the canvas in the widget: + context.set_source_surface(self._drawing_canvas) + context.paint() + + # Return if there is no stroke to paint: + if self._stroke_points == []: + return + + # Get context from widget and paint: + self._set_stroke_context(context) + self._paint_stroke(context, self._stroke_points) diff --git a/paintwithme.py b/paintwithme.py index 41b1fd5..6052cba 100644 --- a/paintwithme.py +++ b/paintwithme.py @@ -17,40 +17,21 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from gettext import gettext as _ - import logging -_logger = logging.getLogger('paintwithme-activity') - -import pygtk -pygtk.require('2.0') - -import gtk -from gtk import gdk -import cairo from sugar.activity import activity -from sugar.graphics.toolbarbox import ToolbarBox -from sugar.activity.widgets import ActivityButton -from sugar.activity.widgets import ActivityToolbox -from sugar.activity.widgets import TitleEntry -from sugar.activity.widgets import StopButton -from sugar.activity.widgets import ShareButton -from sugar.graphics.toolbutton import ToolButton import telepathy -import dbus from dbus.service import signal from dbus.gobject_service import ExportedGObject from sugar.presence import presenceservice from sugar.presence.tubeconn import TubeConnection +from toolbar import PaintToolbar +from drawing import Drawing from utils import json_load, json_dump -# This prevents the expose callback to draw a large stroke each time: -STROKE_MAX_POINTS = 80 - - SERVICE = 'org.sugarlabs.PaintWithMeActivity' IFACE = SERVICE PATH = '/org/augarlabs/PaintWithMeActivity' @@ -63,12 +44,16 @@ class PaintWithMeActivity(activity.Activity): """Init activity, add toolbars and drawing widget.""" super(PaintWithMeActivity, self).__init__(handle) - self._make_toolbar() + toolbar_box = PaintToolbar(self) + self.set_toolbar_box(toolbar_box) + toolbar_box.show() + self._setup_dispatch_table() self._drawing = Drawing(parent=self) self.set_canvas(self._drawing) self._drawing.show() + toolbar_box.set_drawing(self._drawing) self._setup_presence_service() @@ -81,48 +66,6 @@ class PaintWithMeActivity(activity.Activity): self._setup_handle = self.canvas.connect('size_allocate', size_allocate_cb) - def _make_toolbar(self): - """Make activity toolbar.""" - toolbar_box = ToolbarBox() - - activity_button = ActivityButton(self) - toolbar_box.toolbar.insert(activity_button, 0) - activity_button.show() - - title_entry = TitleEntry(self) - toolbar_box.toolbar.insert(title_entry, -1) - title_entry.show() - - share_button = ShareButton(self) - toolbar_box.toolbar.insert(share_button, -1) - share_button.show() - - separator = gtk.SeparatorToolItem() - toolbar_box.toolbar.insert(separator, -1) - separator.show() - - button = ToolButton('edit-clear') - button.set_tooltip(_("Clear canvas")) - button.connect("clicked", self._clear_canvas_cb) - toolbar_box.toolbar.insert(button, -1) - button.show() - - separator = gtk.SeparatorToolItem() - separator.props.draw = False - separator.set_expand(True) - toolbar_box.toolbar.insert(separator, -1) - separator.show() - - stop_button = StopButton(self) - toolbar_box.toolbar.insert(stop_button, -1) - stop_button.show() - - self.set_toolbar_box(toolbar_box) - toolbar_box.show() - - def _clear_canvas_cb(self, button): - self._drawing.clear_canvas() - # Collaboration-related methods below: def _setup_presence_service(self): @@ -147,7 +90,7 @@ class PaintWithMeActivity(activity.Activity): def _new_tube_common(self, sharer): """Joining and sharing are mostly the same...""" if self._shared_activity is None: - _logger.debug("Error: Failed to share or join activity ... \ + logging.debug("Error: Failed to share or join activity ... \ _shared_activity is null in _shared_cb()") return @@ -162,11 +105,11 @@ class PaintWithMeActivity(activity.Activity): 'NewTube', self._new_tube_cb) if sharer: - _logger.debug('This is my activity: making a tube...') + logging.debug('This is my activity: making a tube...') id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( SERVICE, {}) else: - _logger.debug('I am joining an activity: waiting for a tube...') + logging.debug('I am joining an activity: waiting for a tube...') self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) @@ -179,11 +122,11 @@ class PaintWithMeActivity(activity.Activity): def _list_tubes_error_cb(self, e): """Log errors.""" - _logger.debug('Error: ListTubes() failed: %s' % (e)) + logging.debug('Error: ListTubes() failed: %s' % (e)) def _new_tube_cb(self, id, initiator, type, service, params, state): """Create a new tube.""" - _logger.debug('New tube: ID=%d initator=%d type=%d service=%s \ + logging.debug('New tube: ID=%d initator=%d type=%d service=%s \ params=%r state=%d' % (id, initiator, type, service, params, state)) if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE): @@ -212,7 +155,7 @@ params=%r state=%d' % (id, initiator, type, service, params, state)) try: command, payload = event_message.split('|', 2) except ValueError: - _logger.debug('Could not split event message %s' % (event_message)) + logging.debug('Could not split event message %s' % (event_message)) return self._processing_methods[command][0](payload) @@ -262,152 +205,3 @@ class ChatTube(ExportedGObject): @signal(dbus_interface=IFACE, signature='s') def SendText(self, text): self.stack = text - - -class Drawing(gtk.DrawingArea): - def __init__(self, parent): - super(Drawing, self).__init__() - - # Communication with the activity, for sharing: - self._parent = parent - - # This are temporal points stored while the stroke is being - # made. When the stroke is finished (release event) the - # stroke is applied to the canvas. - self._stroke_points = [] - - # Our drawing canvas: - self._canvas = None - - # The settings of our brush: - self._stroke_color = 1.0, 0.0, 0.0, 0.3 - self._stroke_width = 8 - - # Sharing? - self.we_are_sharing = False - - # The masks to capture the events we are interested in: - self.add_events(gdk.EXPOSURE_MASK | gdk.VISIBILITY_NOTIFY_MASK) - self.add_events(gtk.gdk.BUTTON_PRESS_MASK | \ - gtk.gdk.BUTTON_RELEASE_MASK | \ - gtk.gdk.BUTTON1_MOTION_MASK) - - # Connect the callbacks: - self.connect("expose-event", self._expose_cb) - self.connect("button-press-event", self._press_cb) - self.connect("motion-notify-event", self._motion_cb) - self.connect("button-release-event", self._release_cb) - - def setup(self, width, height): - """Setup a blank canvas of specified size.""" - self._canvas = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - context = cairo.Context(self._canvas) - context.rectangle(0, 0, width, height) - context.set_source_rgb(1.0, 1.0, 1.0) - context.fill() - self.queue_draw() - - def set_sharing(self, share=True): - """Set sharing True or False.""" - self.we_are_sharing = share - - def get_size(self): - """Return the size of the current canvas.""" - width = self._canvas.get_width() - height = self._canvas.get_height() - return width, height - - def remote_stroke(self, stroke_points): - """Draw stroke from other player.""" - context = cairo.Context(self._canvas) - self._set_stroke_context(context) - self._paint_stroke(context, stroke_points) - self.queue_draw() - - def clear_canvas(self): - width, height = self.get_size() - self.setup(width, height) - if self.we_are_sharing: - self._parent.send_new_drawing() - - def _set_stroke_context(self, context): - """Set the settings of our brush to the Cairo context.""" - context.set_source_rgba(*self._stroke_color) - context.set_line_width(self._stroke_width) - - def _paint_stroke(self, context, stroke_points): - """Draw lines from the list of points to the Cairo context""" - point_x, point_y = stroke_points[0] - context.move_to(point_x, point_y) - - for point_x, point_y in stroke_points[1:]: - context.line_to(point_x, point_y) - - context.stroke() - - def _send_stroke_to_canvas(self, continue_stroke=False): - """Paint current stroke in the canvas, clean stroke points.""" - - # Return if there is no stroke to paint: - if self._stroke_points == []: - return - - # Get context from canvas and paint: - context = cairo.Context(self._canvas) - self._set_stroke_context(context) - self._paint_stroke(context, self._stroke_points) - - if self.we_are_sharing: - self._parent.send_stroke(self._stroke_points) - - # Clean the list of points: - if continue_stroke: - self._stroke_points = self._stroke_points[-1:] - else: - self._stroke_points = [] - - def _press_cb(self, widget, event): - mouse_x, mouse_y, state = event.window.get_pointer() - - # We paint pressing the BUTTON1, return otherwise: - if not state and gtk.gdk.BUTTON1_MASK: - return - - self._stroke_points.append((mouse_x, mouse_y)) - - def _motion_cb(self, widget, event): - if event.is_hint: - mouse_x, mouse_y, state = event.window.get_pointer() - else: - mouse_x = event.x - mouse_y = event.y - state = event.state - - # We paint pressing the BUTTON1, return otherwise: - if not state and gtk.gdk.BUTTON1_MASK: - return - - # Paint stroke in canvas if it gets too big: - if len(self._stroke_points) > STROKE_MAX_POINTS: - self._send_stroke_to_canvas(continue_stroke=True) - - self._stroke_points.append((mouse_x, mouse_y)) - self.queue_draw() - - def _release_cb(self, widget, event): - self._send_stroke_to_canvas() - - def _expose_cb(self, widget, event): - context = self.window.cairo_create() - - # Paint the canvas in the widget: - context.set_source_surface(self._canvas) - context.paint() - - # Return if there is no stroke to paint: - if self._stroke_points == []: - return - - # Get context from widget and paint: - self._set_stroke_context(context) - self._paint_stroke(context, self._stroke_points) diff --git a/toolbar.py b/toolbar.py new file mode 100644 index 0000000..70d27c5 --- /dev/null +++ b/toolbar.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2012 Manuel Quiñones +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from gettext import gettext as _ +import logging + +import gtk + +from sugar.graphics.toolbarbox import ToolbarBox +from sugar.activity.widgets import ActivityToolbarButton +from sugar.activity.widgets import StopButton +from sugar.graphics.toolbutton import ToolButton + + +class PaintToolbar(ToolbarBox): + def __init__(self, activity): + """Make activity toolbar.""" + super(PaintToolbar, self).__init__() + + self._drawing = None + + activity_button = ActivityToolbarButton(activity) + self.toolbar.insert(activity_button, -1) + activity_button.show() + + separator = gtk.SeparatorToolItem() + self.toolbar.insert(separator, -1) + separator.show() + + button = ToolButton('edit-clear') + button.set_tooltip(_("Clear canvas")) + button.connect("clicked", self._clear_drawing_canvas_cb) + self.toolbar.insert(button, -1) + button.show() + + separator = gtk.SeparatorToolItem() + separator.props.draw = False + separator.set_expand(True) + self.toolbar.insert(separator, -1) + separator.show() + + stop_button = StopButton(activity) + self.toolbar.insert(stop_button, -1) + stop_button.show() + + def set_drawing(self, drawing): + self._drawing = drawing + + def _clear_drawing_canvas_cb(self, button): + logging.debug("clear canvas") + self._drawing.clear_drawing_canvas() -- cgit v0.9.1