diff options
author | dave <drykod@gmail.com> | 2009-10-27 00:31:16 (GMT) |
---|---|---|
committer | dave <drykod@gmail.com> | 2009-10-27 00:31:16 (GMT) |
commit | d3a006f11993435cf9a9e14b59ff626bfa09e7ba (patch) | |
tree | 8c66ad1a1e6138898d4be5e827d15f33d0b485d4 | |
parent | 926238a2c54daae80d4c561b4cda8546d40173a7 (diff) |
added new actions and events
-rw-r--r-- | addons/changecolor.py | 66 | ||||
-rw-r--r-- | addons/intromessage.py | 161 | ||||
-rw-r--r-- | addons/messagebuttonnext.py | 158 | ||||
-rw-r--r-- | addons/openactivity.py | 180 | ||||
-rw-r--r-- | addons/playsound.py | 50 | ||||
-rw-r--r-- | addons/smiley.py | 82 | ||||
-rw-r--r-- | data/icons/smiley.svg | 120 | ||||
-rw-r--r-- | tutorius/overlayer.py | 83 |
8 files changed, 898 insertions, 2 deletions
diff --git a/addons/changecolor.py b/addons/changecolor.py new file mode 100644 index 0000000..ae20e8a --- /dev/null +++ b/addons/changecolor.py @@ -0,0 +1,66 @@ +# Copyright (C) 2009, Tutorius.org +# +# 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 sugar.tutorius.actions import Action + +class ChangeColor(Action): + + def __init__(self, target, color, state): + """Constructor - Change a widget color + @param target: the widget for which you want to change the color + @param color: the color you want the widget to be colorized with \ + (string)(ex: "#ffff00000000", or "red") + @param: state of the widget when you want a color change \ + (ex: gtk.STATE_NORMAL) + @return : nothing! + """ + Action.__init__(self) + self._target = target + self._state = state + #We have to get the initial color in the sugar rc theme + current_style = self._target.rc_get_style() + + self._old_color = current_style.bg[self._state] + self._new_color = gtk.gdk.color_parse(color) + + def do(self): + """Action do + Change the color of the targeted widget with the chosen color + """ + self._target.modify_bg(state,self._new_color) + + def undo(self): + """Action undo + Go back to the original color + """ + self._target.modify_bg(state,self._old_color) + + def enter_editmode(self, *args): + """ + Enters edit mode. The action should display itself in some way, + without affecting the currently running application. + """ + self._target.modify_bg(state,self._new_color) + + def exit_editmode(self, *args): + self._target.modify_bg(state,self._old_color) + +__action__ = { + "name" : "ChangeColor", + "display_name" : "Change widget color", + "icon" : "message-bubble", + "class" : ChangeColor +} + diff --git a/addons/intromessage.py b/addons/intromessage.py new file mode 100644 index 0000000..f4f19a9 --- /dev/null +++ b/addons/intromessage.py @@ -0,0 +1,161 @@ +# Copyright (C) 2009, Tutorius.org +# +# 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 + +import logging + +import gobject + +import gtk, gtk.gdk + +from sugar.tutorius.filters import EventFilter +from sugar.tutorius.properties import TStringProperty, TArrayProperty, TIntProperty +from sugar.tutorius import overlayer +from sugar.tutorius.services import ObjectStore + +from sugar import profile + +# for easy profile access +xo_line_color = profile.get_color().get_stroke_color() +xo_fill_color = profile.get_color().get_fill_color() + +class IntroMessage(EventFilter): + """ + IntroMessage is a special EventFilter that uses gobject + start button to trigger a state change after user click on it. + It must be used inside a gobject main loop to work. + """ + message = TStringProperty("Message") + + def __init__(self, message=None): + """Constructor. + + @param message message to display + """ + super(IntroMessage,self).__init__() + + if message: + self.message = message + + self.overlay = None + self.msgnext = None + + def install_handlers(self, callback, **kwargs): + """install_handlers creates the introduction message and shows it""" + super(IntroMessage,self).install_handlers(callback, **kwargs) + + # get or inject overlayer + self.overlay = ObjectStore().activity._overlayer + + if not self.overlay: + self.overlay = ObjectStore().activity._overlayer + + self.msgnext = None + + #Create the introduction message + if not self.msgnext: + # create an eventbox + self.msgnext = gtk.EventBox() + self.msgnext.set_visible_window(True) + + # create a vbox + self.box = gtk.VBox() + + # get position (center of screen) + screen = gtk.gdk.Screen() + scr_width_half = screen.get_width()/2 #self.overlayer.get_screen().get_width()/2 + scr_height_half = screen.get_height()/2 #self.overlayer.get_screen().get_height()/2 + + # get user name + name = profile.get_nick_name() + if not name or not len(name): + name = "User" + + # create a label + self._label = gtk.Label("Hello " + name + "!\n\n" + self.message) + self._text = "<b>%s</b>" % self.message + self._label.set_markup(self._text) + self._label.set_line_wrap(True) + + self._colortext = gtk.gdk.color_parse("white") + self._label.modify_fg(gtk.STATE_NORMAL, self._colortext) + self._label.modify_fg(gtk.STATE_PRELIGHT, self._colortext) + self._label.modify_fg(gtk.STATE_ACTIVE, self._colortext) + self._label.modify_fg(gtk.STATE_INSENSITIVE, self._colortext) + + self._label.show() + + # create a hbox (holding button) + self._hbox = gtk.HBox() + + # create a button inside hbox + self._btnnext = gtk.Button("START") + self._btnnext.connect("clicked", self.btnnext_clicked) + + self._colorbtn = gtk.gdk.color_parse(xo_fill_color) + + self._btnnext.modify_bg(gtk.STATE_NORMAL, self._colorbtn) + self._btnnext.modify_bg(gtk.STATE_PRELIGHT, self._colorbtn) + self._btnnext.modify_bg(gtk.STATE_ACTIVE, self._colorbtn) + + self._btnnext.show() + + self._hbox.pack_end(self._btnnext, expand=False) + + self._hbox.show() + + self.box.pack_start(self._label, expand=True) + self.box.pack_start(self._hbox, expand=True) + + self.box.show() + + self.msgnext.add(self.box) + + self._colormsgnext = gtk.gdk.color_parse(xo_fill_color) + self.msgnext.modify_bg(gtk.STATE_NORMAL, self._colormsgnext) + + # add space around minimum need size + wid_width, wid_height = self.msgnext.size_request() + self.msgnext.set_size_request(wid_width+40, wid_height+40) + + self.msgnext.show() + + # set position + wid_width, wid_height = self.msgnext.size_request() + self.position = (scr_width_half-wid_width/2, scr_height_half-wid_height/2) + x, y = self.position + + self.overlay.put(self.msgnext, x, y) + + self.overlay.queue_draw() + + def remove_handlers(self): + """remove handler removes the introduction message""" + super(IntroMessage,self).remove_handlers() + + if self.msgnext: + self.msgnext.destroy() + self.msgnext = None + + def btnnext_clicked(self, widget): + self.do_callback() + +__event__ = { + "name" : "IntroMessage", + "display_name" : "Introduction message", + "icon" : "message-bubble", + "class" : IntroMessage, + "mandatory_props" : ["message"] +} diff --git a/addons/messagebuttonnext.py b/addons/messagebuttonnext.py new file mode 100644 index 0000000..ed6dd9d --- /dev/null +++ b/addons/messagebuttonnext.py @@ -0,0 +1,158 @@ +# Copyright (C) 2009, Tutorius.org +# +# 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 + +import logging + +import gobject + +import gtk, gtk.gdk + +from sugar.tutorius.filters import EventFilter +from sugar.tutorius.properties import TStringProperty, TArrayProperty, TIntProperty +from sugar.tutorius import overlayer +from sugar.tutorius.services import ObjectStore + +from sugar import profile + +# for easy profile access +xo_line_color = profile.get_color().get_stroke_color() +xo_fill_color = profile.get_color().get_fill_color() + +class MessageButtonNext(EventFilter): + """ + MessageButtonNext is a special EventFilter that uses gobject + next button to trigger a state change after user click on it. + It must be used inside a gobject main loop to work. + """ + message = TStringProperty("Message") + + # Create the position as an array of fixed-size 2 + position = TArrayProperty((0,0), 2, 2) + + def __init__(self, message=None, position=None): + """Constructor. + + @param message message to display + @param position message position + """ + super(MessageButtonNext,self).__init__() + + if position: + self.position = position + else: + self.position = (300, 200) + + if message: + self.message = message + + self.overlay = None + self.msgnext = None + + def install_handlers(self, callback, **kwargs): + """install_handlers creates the message button next and shows it""" + super(MessageButtonNext,self).install_handlers(callback, **kwargs) + + x, y = self.position + + # get or inject overlayer + self.overlay = ObjectStore().activity._overlayer + + if not self.overlay: + self.overlay = ObjectStore().activity._overlayer + + self.msgnext = None + + #Create the message button next + if not self.msgnext: + # create an eventbox + self.msgnext = gtk.EventBox() + self.msgnext.set_visible_window(True) + + # create a vbox + self.box = gtk.VBox() + + # create a label + self._label = gtk.Label(self.message) + self._text = "<b>%s</b>" % self.message + self._label.set_markup(self._text) + self._label.set_line_wrap(True) + + self._colortext = gtk.gdk.color_parse("white") + self._label.modify_fg(gtk.STATE_NORMAL, self._colortext) + self._label.modify_fg(gtk.STATE_PRELIGHT, self._colortext) + self._label.modify_fg(gtk.STATE_ACTIVE, self._colortext) + self._label.modify_fg(gtk.STATE_INSENSITIVE, self._colortext) + + self._label.show() + + # create a hbox (holding button) + self._hbox = gtk.HBox() + + # create a button inside hbox + self._btnnext = gtk.Button("NEXT") + self._btnnext.connect("clicked", self.btnnext_clicked) + + self._colorbtn = gtk.gdk.color_parse(xo_fill_color) + + self._btnnext.modify_bg(gtk.STATE_NORMAL, self._colorbtn) + self._btnnext.modify_bg(gtk.STATE_PRELIGHT, self._colorbtn) + self._btnnext.modify_bg(gtk.STATE_ACTIVE, self._colorbtn) + + self._btnnext.show() + + self._hbox.pack_end(self._btnnext, expand=False) + + self._hbox.show() + + self.box.pack_start(self._label, expand=True) + self.box.pack_start(self._hbox, expand=True) + + self.box.show() + + self.msgnext.add(self.box) + + self._colormsgnext = gtk.gdk.color_parse(xo_fill_color) + self.msgnext.modify_bg(gtk.STATE_NORMAL, self._colormsgnext) + + # add space around minimum need size + wid_width, wid_height = self.msgnext.size_request() + + self.msgnext.set_size_request(wid_width+40, wid_height+40) + + self.msgnext.show() + + self.overlay.put(self.msgnext, x, y) + + self.overlay.queue_draw() + + def remove_handlers(self): + """remove handler removes the message button next""" + super(MessageButtonNext,self).remove_handlers() + + if self.msgnext: + self.msgnext.destroy() + self.msgnext = None + + def btnnext_clicked(self, widget): + self.do_callback() + +__event__ = { + "name" : "MessageButtonNext", + "display_name" : "Message button next", + "icon" : "message-bubble", + "class" : MessageButtonNext, + "mandatory_props" : ["message"] +} diff --git a/addons/openactivity.py b/addons/openactivity.py new file mode 100644 index 0000000..1b4d4b7 --- /dev/null +++ b/addons/openactivity.py @@ -0,0 +1,180 @@ +# Copyright (C) 2009, Tutorius.org +# +# 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 + +import logging + +import gobject + +import gtk, gtk.gdk + +import os +import dbus + +from sugar.tutorius.filters import EventFilter +from sugar.tutorius.properties import TStringProperty, TArrayProperty, TIntProperty +from sugar.tutorius import overlayer +from sugar.tutorius.services import ObjectStore + +from sugar import profile + +from sugar.activity import activityfactory +from sugar.bundle.activitybundle import ActivityBundle + +# for easy profile access +xo_line_color = profile.get_color().get_stroke_color() +xo_fill_color = profile.get_color().get_fill_color() + +class OpenActivity(EventFilter): + """ + IntroMessage is a special EventFilter that uses gobject + start button to trigger a state change after user click on it. + It must be used inside a gobject main loop to work. + """ + message = TStringProperty("Message") + + def __init__(self, message=None): + """Constructor. + + @param message message to display + """ + super(OpenActivity,self).__init__() + + if message: + self.message = message + + self.overlay = None + self.msgnext = None + + def install_handlers(self, callback, **kwargs): + """install_handlers creates the open activity message and shows it""" + super(OpenActivity,self).install_handlers(callback, **kwargs) + + # get or inject overlayer + self.overlay = ObjectStore().activity._overlayer + + if not self.overlay: + self.overlay = ObjectStore().activity._overlayer + + self.msgnext = None + + #Create the open activity message + if not self.msgnext: + # create an eventbox + self.msgnext = gtk.EventBox() + self.msgnext.set_visible_window(True) + + # create a vbox + self.box = gtk.VBox() + + # get position (center of screen) + screen = gtk.gdk.Screen() + scr_width_half = screen.get_width()/2 #self.overlayer.get_screen().get_width()/2 + scr_height_half = screen.get_height()/2 #self.overlayer.get_screen().get_height()/2 + + # get user name + name = profile.get_nick_name() + if not name or not len(name): + name = "User" + + # create a label + self._label = gtk.Label("Hello " + name + "!\n\n" + self.message) + self._text = "<b>%s</b>" % self.message + self._label.set_markup(self._text) + self._label.set_line_wrap(True) + + self._colortext = gtk.gdk.color_parse("white") + self._label.modify_fg(gtk.STATE_NORMAL, self._colortext) + self._label.modify_fg(gtk.STATE_PRELIGHT, self._colortext) + self._label.modify_fg(gtk.STATE_ACTIVE, self._colortext) + self._label.modify_fg(gtk.STATE_INSENSITIVE, self._colortext) + + self._label.show() + + # create a hbox (holding button) + self._hbox = gtk.HBox() + + # create a button inside hbox + self._btnnext = gtk.Button("OPEN") + self._btnnext.connect("clicked", self.btnnext_clicked) + + self._colorbtn = gtk.gdk.color_parse(xo_fill_color) + + self._btnnext.modify_bg(gtk.STATE_NORMAL, self._colorbtn) + self._btnnext.modify_bg(gtk.STATE_PRELIGHT, self._colorbtn) + self._btnnext.modify_bg(gtk.STATE_ACTIVE, self._colorbtn) + + self._btnnext.show() + + self._hbox.pack_end(self._btnnext, expand=False) + + self._hbox.show() + + self.box.pack_start(self._label, expand=True) + self.box.pack_start(self._hbox, expand=True) + + self.box.show() + + self.msgnext.add(self.box) + + self._colormsgnext = gtk.gdk.color_parse(xo_fill_color) + self.msgnext.modify_bg(gtk.STATE_NORMAL, self._colormsgnext) + + # add space around minimum need size + wid_width, wid_height = self.msgnext.size_request() + self.msgnext.set_size_request(wid_width+40, wid_height+40) + + self.msgnext.show() + + # set position + wid_width, wid_height = self.msgnext.size_request() + self.position = (scr_width_half-wid_width/2, scr_height_half-wid_height/2) + x, y = self.position + + self.overlay.put(self.msgnext, x, y) + + self.overlay.queue_draw() + + def remove_handlers(self): + """remove handler removes the open activity message""" + super(OpenActivity,self).remove_handlers() + + if self.msgnext: + self.msgnext.destroy() + self.msgnext = None + + def btnnext_clicked(self, widget): + # temporary activity name hardcoded + activity_name = "org.laptop.Terminal" + + bus = dbus.SessionBus() + proxy = bus.get_object('org.laptop.Shell', '/org/laptop/Shell') + path = dbus.Interface(proxy, 'org.laptop.Shell').GetBundlePath(activity_name) + if not path: + print 'Cannot find %s bundle.' % activity_name + else: + activity = ActivityBundle(path) + activityfactory.create(activity) + + self.do_callback() + +__event__ = { + "name" : "OpenActivity", + "display_name" : "Open activity message", + "icon" : "message-bubble", + "class" : OpenActivity, + "mandatory_props" : ["message"] +} + diff --git a/addons/playsound.py b/addons/playsound.py new file mode 100644 index 0000000..b657c8a --- /dev/null +++ b/addons/playsound.py @@ -0,0 +1,50 @@ +# Copyright (C) 2009, Tutorius.org +# +# 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 sugar.tutorius.actions import Action + +class PlaySound(Action): + + def __init__(self, target): + """Constructor + @param target: name of the sound you want to play + """ + Action.__init__(self) + self._target = target + + def do(self): + """Action do + Play a sound in the mixer + """ + pygame.mixer.init() + #We wait for a "place" in the mixer + if not pygame.mixer.music.get_busy(): + pygame.mixer.music.load(self._target) + pygame.mixer.music.play() + + def undo(self): + """Action undo + Quit the mixer + """ + pygame.mixer.music.stop() + pygame.mixer.quit() + +__action__ = { + "name" : "PlaySound", + "display_name" : "Play sound", + "icon" : "message-bubble", + "class" : PlaySound +} + diff --git a/addons/smiley.py b/addons/smiley.py new file mode 100644 index 0000000..b50de0c --- /dev/null +++ b/addons/smiley.py @@ -0,0 +1,82 @@ +# Copyright (C) 2009, Tutorius.org +# +# 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 sugar.tutorius.actions import Action +from sugar.tutorius import overlayer +from sugar.tutorius.services import ObjectStore + +class Smiley(Action): + + def __init__(self): + """ + Draw a big smiley at the center of the screen + """ + Action.__init__(self) + + self.overlay = None + self._smiley = None + + def do(self): + """ + Do + """ + # get or inject overlayer + self.overlay = ObjectStore().activity._overlayer + # FIXME: subwindows, are left to overlap this. This behaviour is + # undesirable. subwindows (i.e. child of top level windows) should be + # handled either by rendering over them, or by finding different way to + # draw the overlay. + + if not self.overlay: + self.overlay = ObjectStore().activity._overlayer + if not self._smiley: + self._smiley = overlayer.Smiley() + self._smiley.show() + self.overlay.put(self._smiley, 0, 0) + self.overlay.queue_draw() + + def undo(self): + """ + Undo + """ + if self._smiley: + self._smiley.destroy() + self._smiley = None + + def enter_editmode(self, *args): + """ + Enters edit mode. The action should display itself in some way, + without affecting the currently running application. + """ + if not self.overlay: + self.overlay = ObjectStore().activity._overlayer + + self._smiley = overlayer.Smiley() + self.overlay.put(self._smiley, 0, 0) + self._smiley.show() + + def exit_editmode(self, *args): + if self._smiley: + self.overlay.remove(self._smiley) + self._smiley = None + self.overlay = None + +__action__ = { + "name" : "Smiley", + "display_name" : "Big smiley", + "icon" : "message-bubble", + "class" : Smiley +} + diff --git a/data/icons/smiley.svg b/data/icons/smiley.svg new file mode 100644 index 0000000..9756352 --- /dev/null +++ b/data/icons/smiley.svg @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + height="55px" + id="svg2383" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + inkscape:version="0.46" + sodipodi:docname="smiley.svg" + sodipodi:version="0.32" + version="1.1" + width="55px"> + <defs + id="defs2385"> + <inkscape:perspective + id="perspective2391" + inkscape:persp3d-origin="32 : 21.333333 : 1" + inkscape:vp_x="0 : 32 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="64 : 32 : 1" + sodipodi:type="inkscape:persp3d" /> + </defs> + <sodipodi:namedview + bordercolor="#666666" + borderopacity="1.0" + id="base" + inkscape:current-layer="layer1" + inkscape:cx="36.007299" + inkscape:cy="32" + inkscape:document-units="px" + inkscape:grid-bbox="true" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:window-height="726" + inkscape:window-maximized="0" + inkscape:window-width="897" + inkscape:window-x="145" + inkscape:window-y="31" + inkscape:zoom="5.5" + pagecolor="#ffffff" + showgrid="true" /> + <metadata + id="metadata2388"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:groupmode="layer" + inkscape:label="Layer 1" + transform="translate(0,-9)"> + <path + d="m 18.420265,15.588046 c -6.299941,0 -11.3606278,5.091225 -11.3606278,11.391168 l 0,8.52047 c 0,6.299941 5.0606868,11.360627 11.3606278,11.360628 l 4.306045,0 c -3.990137,3.853923 -9.100719,8.55101 -9.100719,8.55101 l 14.90319,-8.55101 8.215077,0 c 6.299941,0 11.360629,-5.060688 11.360628,-11.360628 l 0,-8.52047 c 0,-6.299941 -5.060686,-11.391168 -11.360628,-11.391168 l -18.323593,0 z" + id="rect2393" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.93177485;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + <path + d="m 15.024328,38.638614 c 1.955367,-5.091599 3.910736,-10.183195 5.866104,-15.274795 0.725882,0 1.451765,0 2.177647,0 2.083874,5.0916 4.167748,10.183196 6.251621,15.274795 -0.767559,0 -1.535119,0 -2.302681,0 -0.593902,-1.542066 -1.187806,-3.084133 -1.781711,-4.6262 -2.129023,0 -4.258049,0 -6.387073,0 -0.559173,1.542067 -1.118345,3.084134 -1.677518,4.6262 -0.715463,0 -1.430926,0 -2.146389,0 z m 4.407394,-6.272461 c 1.726141,0 3.452282,0 5.178425,0 -0.902841,-2.462093 -1.914567,-4.887008 -2.677778,-7.397751 -0.52677,2.55814 -1.634085,4.944943 -2.500647,7.397751 z m 18.504796,4.907524 c -1.838977,1.733981 -5.157492,2.370029 -7.106335,0.497687 -1.652486,-1.767667 -0.397833,-4.8651 1.93833,-5.228082 1.582053,-0.524808 3.967388,-0.117044 5.019523,-1.242503 0.357796,-2.140519 -2.188156,-2.803677 -3.81609,-2.30789 -1.611203,-0.05002 -1.152189,2.566754 -2.696647,1.856331 -1.660075,0.218606 -0.318477,-1.770333 0.269586,-2.358415 1.812428,-1.412953 4.486925,-1.487977 6.565502,-0.656418 1.932898,0.888601 1.522618,3.214011 1.566166,4.96126 0.09528,1.950742 -0.314033,4.039001 0.593905,5.842967 -0.652949,0 -1.305894,0 -1.958842,0 C 38.099047,38.213411 37.986767,37.7444 37.936518,37.273677 z M 37.780227,33.08509 c -1.623868,0.756746 -3.573656,0.44341 -5.123725,1.341496 -1.689943,1.542703 0.669958,3.650795 2.433084,2.927027 1.729527,-0.198499 2.900173,-1.892395 2.690641,-3.580843 0,-0.229227 0,-0.458454 0,-0.68768 z" + id="flowRoot3171" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Arial;-inkscape-font-specification:Arial" /> + <path + sodipodi:type="arc" + style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path2426" + sodipodi:cx="26.181818" + sodipodi:cy="26.636364" + sodipodi:rx="23.272728" + sodipodi:ry="22.363636" + d="M 49.454546,26.636364 A 23.272728,22.363636 0 1 1 2.90909,26.636364 A 23.272728,22.363636 0 1 1 49.454546,26.636364 z" + transform="matrix(1.0988775,0,0,1.1516775,-0.9524328,5.8689525)" /> + <path + sodipodi:type="arc" + style="fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path3207" + sodipodi:cx="18.727272" + sodipodi:cy="17.09091" + sodipodi:rx="3.090909" + sodipodi:ry="3" + d="M 21.818181,17.09091 A 3.090909,3 0 1 1 15.636363,17.09091 A 3.090909,3 0 1 1 21.818181,17.09091 z" + transform="translate(0,9)" /> + <path + sodipodi:type="arc" + style="fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path3209" + sodipodi:cx="18.727272" + sodipodi:cy="17.09091" + sodipodi:rx="3.090909" + sodipodi:ry="3" + d="M 21.818181,17.09091 A 3.090909,3 0 1 1 15.636363,17.09091 A 3.090909,3 0 1 1 21.818181,17.09091 z" + transform="translate(16.363637,8.7272722)" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:#ffff00;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3211" + sodipodi:cx="27.363636" + sodipodi:cy="35.363636" + sodipodi:rx="10.272727" + sodipodi:ry="10" + d="M 37.483395,33.644345 A 10.272727,10 0 1 1 17.259675,33.558445" + transform="translate(0,9)" + sodipodi:start="6.1103977" + sodipodi:end="9.6062922" + sodipodi:open="true" /> + </g> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="eyes" /> +</svg> diff --git a/tutorius/overlayer.py b/tutorius/overlayer.py index 0a3d542..7ebe347 100644 --- a/tutorius/overlayer.py +++ b/tutorius/overlayer.py @@ -58,7 +58,7 @@ class Overlayer(gtk.Layout): @param overlayed widget to be overlayed. Will be resized to full size. """ def __init__(self, overlayed=None): - gtk.Layout.__init__(self) + super(Overlayer, self).__init__() self._overlayed = overlayed if overlayed: @@ -83,7 +83,7 @@ class Overlayer(gtk.Layout): if hasattr(child, "draw_with_context"): # if the widget has the CanvasDrawable protocol, use it. child.no_expose = True - gtk.Layout.put(self, child, x, y) + super(Overlayer, self).put(child, x, y) # be sure to redraw or the overlay may not show self.queue_draw() @@ -151,6 +151,85 @@ class Overlayer(gtk.Layout): # some cases. self._overlayed.set_size_request(allocation.width, allocation.height) +class Smiley(gtk.Widget): + """ + Draw a smiley + """ + def __init__(self): + """ + Creates a new Smiley + """ + gtk.Widget.__init__(self) + + self.__realizer = self.connect_after("realize", self.do_realize_init) + self.connect("size-allocate", self.do_size_allocate_init) + + def draw_with_context(self, context): + # DRAW SMILEY + + scr_width_half = self.get_screen().get_width()/2 + scr_height_half = self.get_screen().get_height()/2 + + # face + context.set_source_rgb(1.0, 1.0, 0.0) # rgb + context.arc(scr_width_half, scr_height_half, 150, 0, 2 * 3.14159) + context.fill() + + # face stroke + context.set_source_rgb(0.0, 0.0, 0.0) # rgb + context.arc(scr_width_half, scr_height_half, 150, 0, 2 * 3.14159) + context.set_line_width(10) + context.stroke() + + # eye + context.set_source_rgb(0.0, 0.0, 0.0) # rgb + context.arc(scr_width_half-50, scr_height_half-40, 25, 0, 2 * 3.14159) + context.fill() + + # eye + context.set_source_rgb(0.0, 0.0, 0.0) # rgb + context.arc(scr_width_half+50, scr_height_half-40, 25, 0, 2 * 3.14159) + context.fill() + + # mouth + context.set_source_rgb(0.0, 0.0, 0.0) # rgb + context.arc(scr_width_half, scr_height_half+40, 60, 0, 3.14159) + context.set_line_width(10) + context.stroke() + + # END OF DRAW SMILEY + + def do_realize(self): + """ Setup gdk window creation. """ + self.set_flags(gtk.REALIZED | gtk.NO_WINDOW) + + self.window = self.get_parent_window() + if not isinstance(self.parent, Overlayer): + assert False, "%s should not realize" % type(self).__name__ + print "Danger, Will Robinson! Rectangle parent is not Overlayer" + + def do_realize_init(self, widget): + """ Setup gdk window creation. """ + self.set_flags(gtk.REALIZED | gtk.NO_WINDOW) + + self.window = self.get_parent_window() + if not isinstance(self.parent, Overlayer): + assert False, "%s should not realize" % type(self).__name__ + print "Danger, Will Robinson! Rectangle parent is not Overlayer" + + ctx = self.window.cairo_create() + + self.draw_with_context(ctx) + + def do_size_allocate(self, allocation): + """Save zone allocated to the widget.""" + self.allocation = allocation + + def do_size_allocate_init(self, widget, allocation): + """Save zone allocated to the widget.""" + self.allocation = allocation + +gobject.type_register(Smiley) class TextBubble(gtk.Widget): """ |