Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Experior.Activity/sbpython.py
diff options
context:
space:
mode:
Diffstat (limited to 'Experior.Activity/sbpython.py')
-rwxr-xr-xExperior.Activity/sbpython.py561
1 files changed, 561 insertions, 0 deletions
diff --git a/Experior.Activity/sbpython.py b/Experior.Activity/sbpython.py
new file mode 100755
index 0000000..b80554f
--- /dev/null
+++ b/Experior.Activity/sbpython.py
@@ -0,0 +1,561 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+sbpython.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 sys
+import time
+import gtk
+import logging
+from gtk import gdk
+import gobject
+from sbdecorators import *
+
+import sugar
+from sugar import graphics
+from sugar.graphics.toolcombobox import ToolComboBox
+
+class NotSupportedError(NotImplementedError):
+ def __init__(self, widget=None, command=None):
+ self.widget = widget
+ self.command = command
+ def __str__(self):
+ return repr('The widget %s [%s] does not support the command(s) %s' \
+ % (self.widget, self.widget.__class__, self.command))
+
+class WidgetDoesNotExist(ValueError):
+ def __init__(self, widget=None):
+ self.widget = widget
+ def __str__(self):
+ return repr('The widget %s could not be identified.' % self.widget)
+
+
+class wrappedWidget(object):
+ raiseExceptions = True
+
+ def __init__(self, widget, name):
+ self.widget = widget
+ self.name = name
+ self.log = logging.getLogger('w(%s)' % name)
+
+ def __getitem__(self, index):
+ """
+ Returns the indexed item.
+
+ For ComboBox and similar items, this will effectively be the n'th object.
+ Uses a helper function for each class type.
+ """
+ indexMethods = \
+ {
+ gtk.ComboBox: self.getList_GtkComboBox,
+ ToolComboBox: self.getList_SugarGraphicsCombo,
+ # gtk.Container: self.getList_Container
+ }
+
+ for classType in indexMethods:
+ if isinstance(self.widget, classType):
+ return indexMethods[classType]()[index]
+
+ def notSupportedError(self, args):
+ """
+ Wrapper for raising NotSupportedError exceptions.
+ """
+ if self.raiseExceptions:
+ raise NotSupportedError, (self.widget, args)
+ return None
+
+ def supportsSignal(self, signalName):
+ """
+ Checks to see if a GTK object supports a signal.
+
+ Checks to see if a GTK object supports a signal, via the
+ gobject.signal_lookup method. Returns True if it is supported,
+ false otherwise.
+ """
+ return gobject.signal_lookup(signalName, self.widget.__class__) != 0
+
+ def click(self):
+ """
+ Simulates a user click.
+
+ Simulates a user click by:
+ 1) Emitting a 'clicked' signal
+ 2) Calling the 'activate' method
+ """
+ self.log.info("Clicking %s" % self.name)
+
+ widgetClass = self.widget.__class__
+
+ clickedSignal = 'clicked'
+ activateMethod = 'activate'
+
+ # Attempt to emit the 'clicked' signal.
+ if self.supportsSignal(clickedSignal):
+ self.widget.emit(clickedSignal)
+ return True
+
+ # Attempt to simply 'activate' the widget.
+ elif hasattr(self.widget, activateMethod):
+ getattr(self.widget, activateMethod)()
+ return True
+
+ # Fail gracefully
+ return self.notSupportedError((clickedSignal, activateMethod))
+
+
+ def getText(self):
+ """
+ Returns the text in the widget.
+
+ Returns whatever text is being stored by the widget. This function
+ gives priority to Widget functions in the following order:
+ 1) get_text method
+ 2) label (same as getLabel)
+ 3) title (same as getTitle)
+ """
+ self.log.info("Getting text for %s" % self.name)
+
+ getText = "get_text"
+
+ # Get the text from the widget
+ # try: return self.__simpleGetter(getText)
+ # except NotSupportedError: pass
+ if hasattr(self.widget, getText):
+ return getattr(self.widget, getText)()
+
+ # Special handling for sugar ToolComboBox
+ if isinstance(self.widget, ToolComboBox):
+ logging.fatal("YES!!!")
+ return self.widget.combo.get_active_item()[0]
+
+ # Try the label
+ try: return self.label
+ except NotSupportedError: pass
+
+ # Try the title
+ try: return self.title
+ except NotSupportedError: pass
+
+ # Fail
+ return self.notSupportedError((getText,"label","title"))
+
+ def typeText(self, val):
+ """
+ Simulates user text entry.
+
+ Simulates a user typing into a widget. Inserts the text at the
+ current insertion location, via:
+ 1) Emits the 'insert-at-cursor' signal.
+ """
+ self.log.info("Adding text for %s to %s" % (self.name, val))
+
+ terminalMethod = "feed_child"
+ insertAtCursor = 'insert-at-cursor'
+
+ if self.supportsSignal(insertAtCursor):
+ self.widget.emit(insertAtCursor, val)
+ return True
+
+ # --- TERMINAL.ACTIVITY HACK ---
+ # vte.Terminal-specific typing
+ if hasattr(self.widget, terminalMethod):
+ self.widget.feed_child(val)
+ return True
+
+ return self.notSupportedError(insertAtCursor, terminalMethod)
+
+ def setText(self, val):
+ """
+ Sets the text for the widget.
+
+ Sets the text for the widget to the user-provided string.
+ Uses the following procedures to attempt to set the text:
+ 1) Calls 'set_text' method
+ 2) Set the 'label' property
+ 3) Set the 'title' property
+ 4) A Terminal-specific method, 'feed_child'
+ 5) Set the text via 'typeText'
+ """
+ self.log.info("Setting text for %s to %s" % (self.name, val))
+
+ setText = "set_text"
+
+ # --- GENERAL APPROACH ---
+ # Try simply setting the text...
+ try:
+ self.__simpleSetter(setText, val)
+ return True
+ except NotSupportedError: pass
+ # if hasattr(self.widget, setText):
+ # getattr(self.widget, setText)(val)
+ # return True
+
+ # Try setting the label
+ try:
+ self.label = val
+ return True
+ except NotSupportedError: pass
+
+ # Try setting the title
+ try:
+ self.title = val
+ return True
+ except NotSupportedError: pass
+
+ # --- TEXT APPENDING ---
+ # Try to just insert the text at the given point...
+ try:
+ self.typeText(val)
+ return True
+ except NotSupportedError:
+ pass
+
+ # Fail
+ return self.notSupportedError((setText,"label","title"))
+
+ def delete(self, numberOfTimes, deleteType):
+ """
+ Simulates user pressing the 'delete' key.
+
+ Simulates user pressing the 'delete' key a given number of times,
+ with a flexible deletion type (e.g. characters, words, lines)
+ """
+ # Note that we must negate numberOfTimes here, because it is
+ # re-negated inside of the 'backspace' call.
+ return backspace(-numberOfTimes, deleteType)
+
+ def backspace(self, numberOfTimes=1, deleteType=gtk.DELETE_CHARS):
+ """
+ Simulates the backspace keypress.
+
+ Simulates the backspace keypress. numberOfTimes times.
+ If numberOfTimes is negative, simulate the 'delete' keypress.
+ """
+ backspace = 'backspace'
+ deleteFromCursor = 'delete-from-cursor'
+
+ # Try the 'deleteFromCursor' approach, as it is very flexible in the
+ # number of ways to delete content.
+ if self.supportsSignal(deleteFromCursor):
+ self.widget.emit(deleteFromCursor, deleteType, -numberOfTimes)
+
+ # Try the standard 'backspace' command. Note that this will not work
+ # for negative quantities.
+ elif self.supportsSignal(backspace) and numberOfTimes >= 0:
+ for i in range(0, numberOfTimes):
+ self.widget.emit(backspace)
+ return True
+
+ else:
+ return self.notSupportedError((backspace, deleteFromCursor))
+
+ def doFocus(self, setFocus=None):
+ """
+ Either sets or gets the focus for the widget.
+ """
+ if setFocus is None:
+ return self.widget.flags() & gtk.HAS_FOCUS
+ elif setFocus:
+ self.widget.grab_focus()
+ return True
+
+ def getInfo(self):
+ """
+ Returns a bunch of information about the widget.
+ """
+ def getClasses(object):
+ """
+ Returns a list containing the class that the object is an instance of,
+ and all of the classes that the instance inherits from.
+ For example:
+ >>> class A: pass
+ >>> class B(A): pass
+ >>> class C(B): pass
+ >>> x = C()
+ >>> list = getClasses(x)
+ >>> list
+ [<class __main__.C at 0x6a1e0>, <class __main__.B at 0x6a1b0>,
+ <class __main__.A at 0x6a180>]
+ >>> for i in list:
+ ... print "Is instance of " + str(i) + "? " + str(isinstance(x,i))
+ ...
+ Is instance of __main__.C? True
+ Is instance of __main__.B? True
+ Is instance of __main__.A? True
+ """
+ # If the passed object is an instance of a class, it will have a
+ # __class__ attribute.
+ if hasattr(object,"__class__"):
+ recursionData = [object.__class__]
+
+ # Include the lowest-level class in the heirarchy tree
+ # Iterate through all of the base classes
+ for _class in object.__class__.__bases__:
+ if not _class is object:
+ recursionData.append(_class)
+ recursionData.extend(getClasses(_class))
+ # Otherwise, the object -is- a class, and it will have a __bases__
+ # attribute.
+ elif hasattr(object,"__bases__"):
+ # Iterate through all of the base classes. Note that we do NOT add
+ # the class itself here, as that is only done above, on the first
+ # call.
+ for _class in object.__bases__:
+ recursionData.append(_class)
+ recursionData.extend(getClasses(_class))
+ return recursionData
+
+ # ------ BEGIN INFO() METHOD -------
+ infoStr = "\n%s" % getClasses(self.widget)
+
+ l = dir(self.widget)
+ for i in l:
+ infoStr+= "\n> %s" % i
+
+ return infoStr
+
+ def __simpleGetter(self, method):
+ """
+ Wrapper for simple 'get' methods
+
+ Wrapper for simple 'get' methods (like get_label), that fails gracefully
+ if the method is not available for the widget object.
+ """
+ self.log.info("Getting %s for %s" % (method, self.name))
+ if hasattr(self.widget, method):
+ return getattr(self.widget, method)
+
+ return self.notSupportedError(method)
+
+ def __simpleSetter(self, method, val):
+ """
+ Wrapper for simple 'set' methods
+
+ Wrapper for simple 'set' methods (like get_label), that fails
+ gracefully if the method is not available for the widget object.
+ """
+ self.log.info("Setting %s to %s for %s" % (method, val, self.name))
+ if hasattr(self.widget, method):
+ getattr(self.widget, method)(val)
+ return True
+
+ return self.notSupportedError(method)
+
+ def getTitle(self):
+ """
+ Returns the widget's title.
+ """
+ return self.__simpleGetter('get_title')
+
+ def setTitle(self, val):
+ """
+ Sets the widget's title
+ """
+ return self.__simpleSetter('set_title', val)
+
+ def getLabel(self):
+ """
+ Returns the widget's label.
+ """
+ return self.__simpleGetter('get_label')
+
+ def setLabel(self, val):
+ """
+ Sets the widget's label
+ """
+ return self.__simpleSetter('set_label', val)
+
+ def getListFormat(self, item):
+ """
+ Converts a ComboBox or ToolComboBox's entries into a list
+
+ Converts a ComboBox or ToolComboBox's entries into a list. This is
+ useful in enumerating the entries in a ComboBox for selection.
+ """
+ retVal = []
+
+ if isinstance(item, ToolComboBox):
+ retVal = self.getList_GtkComboBox(item)
+ elif isinstance(item, gtk.ComboBox):
+ retVal = self.getList_SugarGraphicsCombo()
+
+ return retVal
+
+ def getList_GtkComboBox(self, combo):
+ """
+ getListFormat handler for gtk.ComboBo objects.
+ """
+ index = combo.get_active()
+ if index == -1:
+ index = 0
+
+ model = combo.get_model()
+ row = model.iter_nth_child(None, index)
+
+ if not row:
+ return None
+
+ listOfValues = []
+
+ for i in range(0, len(model)):
+ try:
+ item = model[i]
+ listOfValues.append(item)
+ except IndexError:
+ break
+ return listOfValues
+
+ def getList_SugarGraphicsCombo(self):
+ """
+ getListFormat handler for sugar.graphics.ComboBox objects.
+ """
+ tempList = self.getList_GtkComboBox(self.widget.combo)
+
+ returnList = []
+ for entry in tempList:
+ returnList.append(entry[1])
+
+ return returnList
+
+ def getSelected_SugarGraphicsCombo(self):
+ """
+ getSelected handler for sugar.graphics.combo* objects.
+ """
+ active = self.getSelected_ComboBox(self.widget.combo)
+
+ if len(active) > 1:
+ return active[1]
+ elif len(active) == 1:
+ return active[0]
+ return active
+
+ def getSelected_ComboBox(self, combo=None):
+ """
+ getSelected handler for gtk.ComboBox objects.
+ """
+ if combo is None:
+ combo = self.widget
+
+ index = combo.get_active()
+ if index == -1:
+ index = 0
+
+ row = combo.get_model().iter_nth_child(None, index)
+ if not row:
+ return None
+ return combo.get_model()[row]
+
+ def getSelected(self):
+ """
+ Returns the item selected in a gtk.ComboBox or sugar.graphics.ToolComboBox
+ """
+ self.log.info("Getting selected entry")
+ # Handle for the ComboBox...
+ if isinstance(self.widget, gtk.ComboBox):
+ return self.getSelected_ComboBox(self.widget)
+
+ # Special handling for sugar ToolComboBox
+ if isinstance(self.widget, ToolComboBox):
+ return self.getSelected_SugarGraphicsCombo()
+
+ try: return self.__simpleGetter('get_active_text')
+ except NotSupportedError: pass
+
+ try:
+ i = self.widget.get_active()
+ mod = self.widget.get_model()
+ return mod[i]
+ except:
+ pass
+
+ return self.notSupportedError('get_active_text', 'get_model')
+
+ def setSelected(self, val):
+ """
+ Sets the item selected in a gtk.ComboBox or sugar.graphics.ToolComboBox
+ """
+ self.log.info("Setting selected entry")
+ # If textual: Enumerate all of the selections
+ # If numeric: set selected
+ combo = self.widget
+
+ # Special handling for ToolComboBox
+ if isinstance(self.widget, ToolComboBox):
+ combo = self.widget.combo
+
+ if isinstance(combo, gtk.ComboBox):
+ # If we were provided with a string, get the numerical offset of
+ # the provided entry.
+ if isinstance(val, str):
+ l = self.getListFormat(combo)
+ if val in l:
+ val = l.index(val)
+
+ # Use the index to set the active combobox item
+ if isinstance(val, int):
+ combo.set_active(val)
+ return
+
+ return self.notSupportedError('set_active')
+
+
+ text = property(getText, setText)
+ title = property(getTitle, setTitle)
+ label = property(getLabel, setLabel)
+
+ focus = property(doFocus, doFocus)
+ selected = property(getSelected, setSelected)
+ info = property(getInfo)
+
+
+class widgetRegistry():
+ gui = None
+
+ def __init__(self):
+ self.widgets = {}
+ self.log = logging.getLogger('wrappedWidget')
+ self.log.info("Instantiated widgetRegistry")
+
+ def __getitem__(self, which):
+ """
+ Allows us to select widgets like a dictionary.
+ """
+
+ # If we do not already have the Widget wrapped in a wrappedWidget,
+ # do so now.
+ if which not in self.widgets:
+ widget = self.gui.getWidgetByName(which)
+
+ self.log.info("Fetched widget %s [%s]" % (which, which.__class__))
+
+ if widget is None:
+ raise WidgetDoesNotExist, which
+
+ wrapped = wrappedWidget(widget, which)
+ self.setWidget(which, wrapped)
+
+ # Return the wrappedWidget object.
+ return self.widgets[which]
+
+ def setWidget(self, which, value):
+ self.widgets[which]=value
+
+# Create the 'sbwidgets' instance.
+if not globals().has_key("sbwidgets"):
+ sbwidgets = widgetRegistry() \ No newline at end of file