diff options
Diffstat (limited to 'Experior.Activity/sbgui.py')
-rwxr-xr-x | Experior.Activity/sbgui.py | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/Experior.Activity/sbgui.py b/Experior.Activity/sbgui.py new file mode 100755 index 0000000..3593f38 --- /dev/null +++ b/Experior.Activity/sbgui.py @@ -0,0 +1,321 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +sbgui.py + +This file is part of sugarbot. + +sugarbot 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. + +sugarbot 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 sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +import gobject +import os +import pygtk +import sys +import time +pygtk.require('2.0') +import gtk +import logging +import sugar + +from gobject import idle_add +from gtk import gdk +from gtk.gdk import event_handler_set +from sbexecutionengine import sbExecutionEngine +from sugar import graphics +from sugar.graphics.toolbutton import Palette +from widgetIdentifier import * + +class sbGUI(gobject.GObject): + """ + Responsible for tracking all identifiable* widgets instantiated by GTK. + + *Identifiable widgets are widgets for which a valid identifier can be + found, via any of the identifiers enumerated by widgetIdentifier.identifiers. + """ + def __init__(self,sugarbotInstance,rpcServer): + """ + Initialize internal variables, and register self as an event handler. + """ + # Necessary, due to inheritance + gobject.GObject.__init__(self) + + # Get the RPC server connection and the sugarbot instance, as well + # as the execution engine. + self.sugarbot = sugarbotInstance + self.rpc = rpcServer + self.engine = sbExecutionEngine(self,self.rpc) + self.log = logging.getLogger('sbGUI') + + # Keep track of the amount of idle time... + self.idletime = 0 + self.idletimeout= 6 + + # In order to keep track of all of the windows, we set up a + # dict of Windows. For each window, the key is its id (as provided + # by id(theWindowObject). The value of each dictionary entry is a + # dictionary of Widgets, set up the same way -- the id() of the + # widget is the key, and the + self.trackedWidgets = {} # Maps id(object) -> widget + self.names = {} # Maps identifier -> widget + + # Register one of our methods to intercept all GTK events + self.registerEventHandler() + + + def eventHandler(self,event=None): + """ Intercepts all GDK events. We then send them off to a separate + handler method (to keep things clean), and then have GTK execute + whatever the event is supposed to do. + """ + if event is not None: + gtk.main_do_event(event) + self.handleEvent(event) + + if event.type is not gtk.gdk.EXPOSE: + self.idletime = 0 + + return True + + + def getWidgetIdentifier(self,widget): + """ + Returns the Widget's unique identifier (for example, a button's + label, or a gkt.Entry's name), or None if it does not have one. + Also, this function filters out many uninitialized identifiers, + such as "GtkToolbar" or "GtkButton" + @param widget - The widget whose identifier is to be retrieved. + """ + if not isinstance(widget,gtk.Widget): + raise "_getWidgetName must take a gtk.Widget object as its argument" + + # ---- GENERIC APPROACH ---- + # Assuming that the widget was named explicitly by the developers, + # getting the name should be very straightforward, with no specialized + # cases or special name-detecting. + widId = widgetIdentifier(widget) + ident = widId.getIdentifier() + + if ident is not None: + return ident + + # ---- SPECIALIZED APPROACH ---- + # Check to see if we have an identifier for the specific type + # of widget before we iterate through all of the different identifiers + # hoping to get a hit. + if widgetIdentifier.identifiers.has_key(widget.__class__): + widId = widgetIdentifier.identifiers[widget.__class__](widget) + ident = widId.getIdentifier() + + if ident is not None: + return ident + + # ---- BRUTE FORCE ---- + # The widget was not named explicitly by the developers, and we do + # not have a case for the specific widget class. + # Iterate through all of our potential identifiers, since we + # very likely have a widget class that it inherits from. + # This method is undesirable, since the identifiers in the dictionary + # widgetIdentifiers may be in a different order each time the + # program is run, due to the non-ordered nature of dictionaries. + for identifier in widgetIdentifier.identifiers: + if isinstance(widget, identifier): + widId = widgetIdentifier.identifiers[identifier](widget) + ident = widId.getIdentifier() + + if ident is not None: + break + + # At this point, we either have a valid widget identifier, or None. + return ident + + + def addWidget(self,widget): + """ + Add a widget to be tracked internally. Widgets are tracked by their + id(). Additionally, add it to the names{} dictionary, so that we + can quickly look up widgets by the name. + """ + + # Don't do anything if we already have this widget by ID + if self.trackedWidgets.has_key(id(widget)): + return + + # Make sure we are working on a widget + if not isinstance(widget, gtk.Widget): + return + + # Containers might have children. Check them all. + if isinstance(widget, gtk.Container): # gtk.Container can have + for child in widget.get_children(): # any number of children + self.addWidget(child) + + if isinstance(widget, gtk.Bin): # gtk.Bin can only have + self.addWidget(widget.get_child()) # one child. + + if isinstance(widget, gtk.Notebook): # gtk.Notebook can have + numPages = widget.get_n_pages() # many children. + for count in range(0, numPages): + page = widget.get_nth_page(count) + self.addWidget(page) + return + + + # Get the widget's identifier & id. If the widget cannot be reliably + # identified, there is no use in tracking it. + identifier = self.getWidgetIdentifier(widget) + _id = id(widget) + if identifier is None: + return + + # Simply keep track of the widgets by ID + self.trackedWidgets[_id]=widget + + # Keep track of the widgets by identifier. + # Check to see if it's already being tracked. + if self.names.has_key(identifier): + raise KeyError, "Already tracking a widget by identifier %s" \ + % identifier + + # Track the little bugger + else: + self.log.debug("Tracking widget id %i by identifier %s" % (_id, identifier)) + self.names[identifier] = widget + + + def delWidget(self,widget): + """ + Remove a widget from internal tracking. Widgets can be removed by + their id() or by the gtk.Widget object. + + TODO: I don't think this function ever gets called. + """ + if not isinstance(widget, gtk.Widget): + raise "Called delWidget on non-Widget object" + return + + # Get id(widget) + identifier = self.getWidgetIdentifier(widget) + _id = id(widget) + + # Remove the widget from the trackedWidgets dict if it is tracked. + if self.trackedWidgets.has_key(_id): + del self.trackedWidgets[_id] + del self.names[identifier] + + else: # Tried to call delWidget on non-tracked Widget + return + + def getWidgetByName(self,widgetName): + """ + Checks to see if we are tracking a Widget with a given name. + If so, return the Widget. If not, return None. + """ + if widgetName in self.names: + return self.names[widgetName] + return None + + def handleEvent_firehose(self,event): + """ + For exploratory testing. Don't bother trying to figure out what this + does, it will change without notification. + Pretty much just outputs a firehose of events. Useful for exploratory + testing, and that's pretty much it. + """ + if not hasattr(self,'classes'): + self.classes = [] + + eventType = event.type + print "----------------------------------------" + print event.type + if event.window: + # print event.window + try: + widget = event.window.get_user_data() + print widget.__class__ + self.classes[widget.__class__] = 1 + except: + raise + print "----------------------------------------" + print "" + print "" + + def handleEvent(self,event): + """ + Handles all GDK events. We first filter them so that we know + they pertain to a window, and then we further drill down based on the + type of action. This method is used to achieve two goals: + [1] Build a database of all windows and widgets + [2] Allow us to see actions as they happen. This could lead to + recording functionality in the future. + """ + try: + # Does the event have a Window that it belongs to? + if (not event.window): + return + + # Get some information on the widget. If it doesn't work, just + # gracefully fail. Exceptions happen with the following events: + # (maybe more, but these are what I've observed): + # - GDK_OWNER_CHANGE + widget = event.window.get_user_data() + eventType = event.type + + # -------- HANDLE WIDGET INSTANTIATION -------- + # MAP events are generated when a widget is initially displayed + # on the screen. In most cases, any naming or configuration that + # is going to be performed *has been* performed. + if eventType == gdk.MAP: + self.addWidget(widget) + + # -------- HANDLE WIDGET DESTRUCTION -------- + # UNMAP events are generated when a widget + # is being taken off the screen. Generally, this will happen at + # the end of an application's execution. However, to maintain + # flexibility (e.g. the possibility of dialog windows), handle + # the UNMAP event here. + elif eventType == gdk.UNMAP: + self.delWidget(widget) + except ValueError: + pass + except: # Oops! + raise + + def idleHandler(self, event=None): + + if self.engine.isComplete(): + return False + + if self.idletime is 0: + self.idletime = time.time() + self.lasttime = self.idletime + + if self.lasttime + 1 < time.time(): + self.lasttime += 1 + + if self.idletime + self.idletimeout < time.time(): + self.engine.executePy() + self.idletime = 0 + + return True + + def registerEventHandler(self): + """ + Registers the method self.eventHandler as the function that + will receive all GDK events. This allows us to snoop on GDK. + """ + if not event_handler_set: + raise NotImplementedError + else: + gobject.idle_add(self.idleHandler) + event_handler_set(self.eventHandler) |