diff options
-rw-r--r-- | addons/bubblemessage.py | 1 | ||||
-rw-r--r-- | addons/bubblemessagewimg.py | 1 | ||||
-rw-r--r-- | addons/changecolor.py | 7 | ||||
-rw-r--r-- | addons/gtkwidgeteventfilter.py | 9 | ||||
-rw-r--r-- | tests/probetests.py | 3 | ||||
-rw-r--r-- | tutorius/TProbe.py | 117 | ||||
-rw-r--r-- | tutorius/constants.py | 2 | ||||
-rw-r--r-- | tutorius/editor.py | 54 | ||||
-rw-r--r-- | tutorius/overlayer.py | 69 |
9 files changed, 237 insertions, 26 deletions
diff --git a/addons/bubblemessage.py b/addons/bubblemessage.py index 7e91d00..53387bf 100644 --- a/addons/bubblemessage.py +++ b/addons/bubblemessage.py @@ -76,6 +76,7 @@ class BubbleMessage(Action): Destroy the dialog """ if self._bubble: + self.overlay.remove(self._bubble) self._bubble.destroy() self._bubble = None diff --git a/addons/bubblemessagewimg.py b/addons/bubblemessagewimg.py index 0ad444f..514a311 100644 --- a/addons/bubblemessagewimg.py +++ b/addons/bubblemessagewimg.py @@ -79,6 +79,7 @@ class BubbleMessageWImg(Action): Destroy the dialog """ if self._bubble: + self.overlay.remove(self._bubble) self._bubble.destroy() self._bubble = None diff --git a/addons/changecolor.py b/addons/changecolor.py index 460da32..eac891a 100644 --- a/addons/changecolor.py +++ b/addons/changecolor.py @@ -63,9 +63,14 @@ class ChangeColor(Action): if not "activity" in kwargs: raise TypeError("activity argument is Mandatory") + activity = kwargs["activity"] + + if not "probe" in kwargs: + raise TypeError("probe argument is Mandatory") + probe = kwargs["probe"] # get widget instance - self.wid = find_widget(kwargs["activity"], self.widaddr, ignore_errors=False) + self.wid = probe.find_widget(activity, self.widaddr, ignore_errors=False) if not self.wid: raise NameError("widget not found") diff --git a/addons/gtkwidgeteventfilter.py b/addons/gtkwidgeteventfilter.py index ac14399..f6ecf86 100644 --- a/addons/gtkwidgeteventfilter.py +++ b/addons/gtkwidgeteventfilter.py @@ -45,9 +45,14 @@ class GtkWidgetEventFilter(EventFilter): super(GtkWidgetEventFilter, self).install_handlers(callback, **kwargs) if not "activity" in kwargs: raise TypeError("activity argument is Mandatory") - + activity = kwargs["activity"] + + if not "probe" in kwargs: + raise TypeError("probe argument is Mandatory") + probe = kwargs["probe"] + #find the widget and connect to its event - self._widget = find_widget(kwargs["activity"], self.object_id) + self._widget = probe.find_widget(activity, self.object_id) self._handler_id = self._widget.connect( \ self.event_name, self.do_callback ) diff --git a/tests/probetests.py b/tests/probetests.py index 17c6afc..357d223 100644 --- a/tests/probetests.py +++ b/tests/probetests.py @@ -187,7 +187,8 @@ class ProbeTest(unittest.TestCase): #Setup the activity and probe self.activity = MockActivity() - self.probe = TProbe(self.activity, MockServiceProxy()) + self.probe = TProbe(self.activity, self.activity.get_bundle_id(), + self.activity.get_id(), service_proxy=MockServiceProxy()) #Override the eventOccured on the Probe... self.old_eO = self.probe.eventOccured diff --git a/tutorius/TProbe.py b/tutorius/TProbe.py index 2834f0c..acba26f 100644 --- a/tutorius/TProbe.py +++ b/tutorius/TProbe.py @@ -33,6 +33,7 @@ from . import properties from .services import ObjectStore from .dbustools import save_args, ignore, logError +from .gtkutils import find_widget, raddr_lookup import copy """ @@ -57,11 +58,13 @@ class TProbe(dbus.service.Object): a DBUS Interface. """ - def __init__(self, activity, service_proxy=None): + def __init__(self, activity, activity_name, unique_id, service_proxy=None): """ Create and register a TProbe for an activity. @param activity activity reference, must be a gtk container + @param activity_name generic name for the activity + @param unique_id specific name for this instance @param service_proxy A Service proxy object to do the registering """ # Moving the ObjectStore assignment here, in the meantime @@ -77,8 +80,8 @@ class TProbe(dbus.service.Object): ObjectStore().activity = activity - self._activity_name = activity.get_bundle_id() - self._unique_id = activity.get_id() + self._activity_name = activity_name + self._unique_id = unique_id LOGGER.debug("TProbe :: Creating TProbe for %s (%d)", self._activity_name, os.getpid()) LOGGER.debug("TProbe :: Current gobject context: %s", str(gobject.main_context_default())) @@ -94,7 +97,7 @@ class TProbe(dbus.service.Object): self._installedActions = {} self._subscribedEvents = {} - LOGGER.debug("TProbe :: registering '%s' with unique_id '%s'", self._activity_name, activity.get_id()) + LOGGER.debug("TProbe :: registering '%s' with unique_id '%s'", self._activity_name, self._unique_id) self._service_proxy.register_probe(self._activity_name, self._unique_id) def start(self): @@ -149,7 +152,7 @@ class TProbe(dbus.service.Object): action._props.update(loaded_action._props) if not is_editing: - action.do(activity=self._activity) + action.do(activity=self._activity, probe=self) else: action.enter_editmode() action.set_notification_cb(partial(self.update_action, address)) @@ -229,7 +232,7 @@ class TProbe(dbus.service.Object): def callback(*args): self.notify(eventfilter) - eventfilter.install_handlers(callback, activity=self._activity) + eventfilter.install_handlers(callback, activity=self._activity, probe=self) name = self._generate_event_reference(eventfilter) self._subscribedEvents[name] = eventfilter @@ -305,6 +308,108 @@ class TProbe(dbus.service.Object): return name + str(suffix) + # ------------------ Helper functions specific to a component -------------- + def find_widget(self, base, path, ignore_errors=True): + """ + Finds a widget from a base object. Symmetric with retrieve_path + + @param base the parent widget + @param path fqdn-style target object name + + @return widget found + """ + return find_widget(base, path, ignore_errors) + + def retrieve_path(self, widget): + """ + Retrieve the path to access a specific widget. + Symmetric with find_widget. + + @param widget the widget to find a path for + + @return path to the widget + """ + return raddr_lookup(widget) + +class FrameProbe(TProbe): + """ + Identical to the base probe except that helper functions are redefined + to handle the four windows that are part of the Frame. + """ + # ------------------ Helper functions specific to a component -------------- + def find_widget(self, base, path, ignore_errors=True): + """ + Finds a widget from a base object. Symmetric with retrieve_path + + format for the path for the frame should be: + + frame://<panel>/<path> + where panel: top | bottom | left | right + path: number[.number]* + + @param base the parent widget + @param path fqdn-style target object name + + @return widget found + """ + protocol, p = path.split("://") + assert protocol == "frame" + + window, object_id = p.split("/") + if window == "top": + return find_widget(base._top_panel, object_id, ignore_errors) + elif window == "bottom": + return find_widget(base._bottom_panel, object_id, ignore_errors) + elif window == "left": + return find_widget(base._left_panel, object_id, ignore_errors) + elif window == "right": + return find_widget(base._right_panel, object_id, ignore_errors) + else: + raise RuntimeWarning("Invalid frame panel: '%s'"%window) + + return find_widget(base, path, ignore_errors) + + def retrieve_path(self, widget): + """ + Retrieve the path to access a specific widget. + Symmetric with find_widget. + + format for the path for the frame should be: + + frame://<panel>/<path> + where panel: top | bottom | left | right + path: number[.number]* + + @param widget the widget to find a path for + + @return path to the widget + """ + name = [] + child = widget + parent = widget.parent + while parent: + name.append(str(parent.get_children().index(child))) + child = parent + parent = child.parent + + name.append("0") # root object itself + name.reverse() + + window = "" + if parent._position == gtk.POS_TOP: + window = "top" + elif parent._position == gtk.POS_BOTTOM: + window = "bottom" + elif parent._position == gtk.POS_LEFT: + window = "left" + elif parent._position == gtk.POS_RIGHT: + window = "right" + else: + raise RuntimeWarning("Invalid root panel in frame: %s"%str(parent)) + + return "frame://"+window+"/"+(".".join(name)) + + class ProbeProxy: """ ProbeProxy is a Proxy class for connecting to a remote TProbe. diff --git a/tutorius/constants.py b/tutorius/constants.py new file mode 100644 index 0000000..377b4a5 --- /dev/null +++ b/tutorius/constants.py @@ -0,0 +1,2 @@ +TARGET_TYPE_WIDGET = 81 +WIDGET_ID = "widget" diff --git a/tutorius/editor.py b/tutorius/editor.py index 9d2effe..7f2cc8e 100644 --- a/tutorius/editor.py +++ b/tutorius/editor.py @@ -23,9 +23,11 @@ import gobject #import gconf from gettext import gettext as _ +from sugar.graphics.window import Window from .gtkutils import register_signals_numbered, get_children + class WidgetIdentifier(gtk.Window): """ Tool that allows identifying widgets. @@ -126,12 +128,26 @@ class WidgetIdentifier(gtk.Window): typecol = gtk.TreeViewColumn(_("Widget"), typerendr, text=1, background=1, foreground=1) explorer.append_column(typecol) - self.__populate_treestore( - tree, #tree - tree.append(None, ["0",self._activity.get_name()]), #parent - self._activity, #widget - "0" #path - ) + + if isinstance(self._activity, Window): + self.__populate_treestore( + tree, #tree + tree.append(None, ["0",str(self._activity)]), #parent + self._activity, #widget + "0" #path + ) + else: + # Assume it is the frame + for win in [self._activity._left_panel,\ + self._activity._right_panel,\ + self._activity._top_panel,\ + self._activity._bottom_panel]: + self.__populate_treestore( + tree, #tree + tree.append(None, ["0",str(self._activity)]), #parent + win, #widget + "0" #path + ) explorer.set_expander_column(typecol) @@ -158,13 +174,25 @@ class WidgetIdentifier(gtk.Window): typecol2 = gtk.TreeViewColumn(_("Widget"), typerendr2, text=1, background=1, foreground=1) explorer2.append_column(typecol2) - self.__populate_gobject_treestore( - tree2, #tree - tree2.append(None, ["activity",self._activity.get_name()]), #parent - self._activity, #widget - "activity" #path - ) - + if isinstance(self._activity, Window): + self.__populate_gobject_treestore( + tree2, #tree + tree2.append(None, ["activity",str(self._activity)]), #parent + self._activity, #widget + "activity" #path + ) + else: + # Assume it is the frame + for win in [self._activity._left_panel,\ + self._activity._right_panel,\ + self._activity._top_panel,\ + self._activity._bottom_panel]: + self.__populate_gobject_treestore( + tree2, #tree + tree2.append(None, ["activity",str(self._activity)]), #parent + win, #widget + "activity" #path + ) explorer2.set_expander_column(typecol2) swd3 = gtk.ScrolledWindow() diff --git a/tutorius/overlayer.py b/tutorius/overlayer.py index 9e4adbf..0b78c53 100644 --- a/tutorius/overlayer.py +++ b/tutorius/overlayer.py @@ -25,6 +25,7 @@ import pangocairo from math import pi from sugar import profile +from .constants import * # for easy profile access from cairo color = profile.get_color().get_stroke_color() @@ -152,6 +153,61 @@ class Overlayer(gtk.Layout): if self._overlayed: self._overlayed.set_size_request(allocation.width, allocation.height) +class FrameOverlayer(gtk.Window): + def __init__(self): + gtk.Window.__init__(self) + self._vbox = gtk.VBox() + self._overlayer = Overlayer(self._vbox) + self.add(self._overlayer) + self._vbox.show() + self._overlayer.show() + self.show_all() + + toCanvas = [ ( WIDGET_ID, 0, TARGET_TYPE_WIDGET ) ] + self._overlayer.drag_dest_set(gtk.DEST_DEFAULT_MOTION | + gtk.DEST_DEFAULT_HIGHLIGHT | + gtk.DEST_DEFAULT_DROP, + toCanvas, gtk.gdk.ACTION_MOVE) + + self._widgets = [] + + + def show(self): + self.set_decorated(False) # Remove borders and title bar + self.set_keep_above(True) # Always on top + self.fullscreen() # Cover the entire screen + + gtk.Window.show(self) + self.expose = self.connect("expose-event", self.apply_mask) + + def apply_mask(self,*args): + self.px = gtk.gdk.Pixmap(None, 1173, 800, 1) # source, size, colors + self.cr = self.px.cairo_create() + self.cr.set_operator(cairo.OPERATOR_CLEAR) + self.cr.paint() + self.cr.set_source_rgb(1,1,1) + self.cr.set_operator(cairo.OPERATOR_SOURCE) + + for widget in self._widgets: + widget.draw_with_context(self.cr) + self.shape_combine_mask(self.px, 0, 0) # pixmap, offset + + def put(self,widget, offset_x, offset_y): + self._widgets.append(widget) + widget.show() + self._overlayer.put(widget, offset_x, offset_y) + self._overlayer.queue_draw() + + def move(self, widget, x, y): + self._overlayer.move(widget,x,y) + + def remove(self, widget): + self._widgets.remove(widget) + self._overlayer.remove(widget) + + def queue_draw(self): + self._overlayer.queue_draw() + class TextBubble(gtk.Widget): """ A CanvasDrawableWidget drawing a round textbox and a tail pointing @@ -424,13 +480,20 @@ class TextBubbleWImg(gtk.Widget): #ct = cairo.Context(surface) # paint image + img_upper_left_x = int((self.allocation.width-self.imgsize[0])/2) + img_upper_left_y = int(self.line_width+self.padding/2) context.set_source_pixbuf( self.pixbuf, - int((self.allocation.width-self.imgsize[0])/2), - int(self.line_width+self.padding/2)) + img_upper_left_x, + img_upper_left_y) + # Set a rectangle + context.rectangle(img_upper_left_x, img_upper_left_y, + self.imgsize[0], self.imgsize[1]) + context.clip() context.paint() - + context.reset_clip() + # work done. Be kind to next cairo widgets and reset matrix. context.identity_matrix() |