Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErick Lavoie <erick.lavoie@gmail.com>2009-09-01 21:44:15 (GMT)
committer Erick Lavoie <erick.lavoie@gmail.com>2009-09-01 21:44:15 (GMT)
commitb358f45f48238446727ed9144c0db2302462ec3d (patch)
treecfe860185874602359e45b84a7f4ff0095171a6a
parenta188dac0527803edb46eabce04100f1c741a96f3 (diff)
Original commit for the changes made for the new API for probesHEADmaster
-rw-r--r--addons/EmbeddedInterpreter.py30
-rw-r--r--addons/WidgetIdentifier.py35
-rw-r--r--addons/eventgenerator.py60
-rw-r--r--tests/inject.py57
-rw-r--r--toolkitfix/activity/activity.py10
-rw-r--r--toolkitfix/graphics/window.py7
-rwxr-xr-xtutorius/TProbe.py259
-rw-r--r--tutorius/actions.py10
-rw-r--r--tutorius/editor_interpreter.py105
-rw-r--r--tutorius/events.py36
-rw-r--r--tutorius/ipython_view.py301
-rw-r--r--tutorius/properties.py7
12 files changed, 911 insertions, 6 deletions
diff --git a/addons/EmbeddedInterpreter.py b/addons/EmbeddedInterpreter.py
new file mode 100644
index 0000000..8c3522e
--- /dev/null
+++ b/addons/EmbeddedInterpreter.py
@@ -0,0 +1,30 @@
+from sugar.tutorius.actions import Action
+from sugar.tutorius.editor_interpreter import EditorInterpreter
+from sugar.tutorius.services import ObjectStore
+
+class EmbeddedInterpreter(Action):
+ def __init__(self):
+ Action.__init__(self)
+ self.activity = None
+ self._dialog = None
+
+ def do(self):
+ os = ObjectStore()
+ if os.activity:
+ self.activity = os.activity
+
+ self._dialog = EditorInterpreter(self.activity)
+ self._dialog.show()
+
+
+ def undo(self):
+ if self._dialog:
+ self._dialog.destroy()
+
+__action__ = {
+ "name" : "EmbeddedInterpreter",
+ "display_name" : "Embedded Interpreter",
+ "icon" : "message-bubble",
+ "class" : EmbeddedInterpreter,
+ "mandatory_props" : []
+}
diff --git a/addons/WidgetIdentifier.py b/addons/WidgetIdentifier.py
new file mode 100644
index 0000000..3c559b5
--- /dev/null
+++ b/addons/WidgetIdentifier.py
@@ -0,0 +1,35 @@
+from sugar.tutorius.actions import Action
+from sugar.tutorius.editor import WidgetIdentifier as WIPrimitive
+from sugar.tutorius.services import ObjectStore
+
+class WidgetIdentifier(Action):
+ def __init__(self):
+ Action.__init__(self)
+ self.activity = None
+ self._dialog = None
+
+ def do(self):
+ os = ObjectStore()
+ if os.activity:
+ self.activity = os.activity
+
+ self._dialog = WIPrimitive(self.activity)
+ self._dialog.show()
+
+
+ def undo(self):
+ if self._dialog:
+ # TODO elavoie 2009-07-19
+ # We should disconnect the handlers, however there seems to be an error
+ # saying that the size of the dictionary changed during the iteration
+ # We should investigate this
+ #self._dialog._disconnect_handlers()
+ self._dialog.destroy()
+
+__action__ = {
+ "name" : "WidgetIdentifier",
+ "display_name" : "Widget Identifier",
+ "icon" : "message-bubble",
+ "class" : WidgetIdentifier,
+ "mandatory_props" : []
+}
diff --git a/addons/eventgenerator.py b/addons/eventgenerator.py
new file mode 100644
index 0000000..a91ccf4
--- /dev/null
+++ b/addons/eventgenerator.py
@@ -0,0 +1,60 @@
+# 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 *
+from sugar.tutorius.gtkutils import find_widget
+
+class EventGenerator(Action):
+ source = TUAMProperty("")
+ type = TStringProperty("clicked")
+
+ def __init__(self, source=None, type="clicked"):
+ Action.__init__(self)
+
+ if source != None:
+ self.source = source
+
+ if type != "clicked":
+ self.type = type
+
+ def do(self):
+ self._activity = ObjectStore().activity
+
+ # TODO elavoie 2009-07-25 We should eventually use the UAM mecanism
+ widget = find_widget(self._activity, self.source)
+
+
+ # TODO elavoie 2009-07-25 We assume a gtk activity, it might
+ # get messier with other widget systems
+
+ # Call the signal on the widget
+ # We use introspection here to obtain the
+ # method that will send the corresponding
+ # signal on the gtk object
+ getattr(widget, self.type)()
+
+ # That's all!!!
+
+ def undo(self):
+ pass
+
+__action__ = {
+ "name" : "EventGenerator",
+ "display_name" : "Event Generator",
+ "icon" : "message-bubble",
+ "class" : EventGenerator,
+ "mandatory_props" : ["source", "type"]
+}
+
diff --git a/tests/inject.py b/tests/inject.py
new file mode 100644
index 0000000..d69d6ff
--- /dev/null
+++ b/tests/inject.py
@@ -0,0 +1,57 @@
+#Test event injection
+
+import gtk
+import gobject
+import time
+import types
+
+class ClickMaster():
+ def __init__(self):
+ self.event = None
+
+ def connect(self, button):
+ self.id = button.connect("pressed",self.capture_event)
+ self.id2 = button.connect("released",self.capture_event2)
+ self.id3 = button.connect("clicked",self.capture_event3)
+ self.button = button
+
+ def capture_event(self, *args):
+ print "Capture Event"
+ print args
+ self.eventPress = args[-1]
+ return False
+
+ def capture_event2(self, *args):
+ print "Capture Release"
+ print args
+ self.eventReleased = args[-1]
+ return False
+
+ def capture_event3(self, *args):
+ print "Capture Clicked"
+ print args
+ self.eventClicked = args[-1]
+ return False
+
+ def inject_event(self):
+ print "Injecting"
+ print self.event
+ #self.event.put()
+ self.button.emit("button_press_event", self.event)
+
+def print_Event(event):
+ for att in dir(event):
+ if not isinstance(att, types.MethodType):
+ print att, getattr(event, att)
+
+if __name__=='__main__':
+ w = gtk.Window()
+ b = gtk.CheckButton("Auto toggle!")
+ c=ClickMaster()
+ w.add(b)
+ b.show()
+ c.connect(b)
+
+ w.show()
+
+ gtk.main()
diff --git a/toolkitfix/activity/activity.py b/toolkitfix/activity/activity.py
index 801ae96..0426527 100644
--- a/toolkitfix/activity/activity.py
+++ b/toolkitfix/activity/activity.py
@@ -79,6 +79,9 @@ from sugar import wm
from sugar.tutorius.creator import Creator
from sugar.tutorius.services import ObjectStore
from sugar.tutorius.bundler import TutorialStore, XMLSerializer
+# Tutorius: For probe support
+from sugar.tutorius import TProbe
+
_ = lambda msg: gettext.dgettext('sugar-toolkit', msg)
@@ -512,8 +515,8 @@ class Activity(Window, gtk.Container):
"""
Window.__init__(self)
-
- ObjectStore().activity = self
+
+ #ObjectStore().activity = self
# process titles will only show 15 characters
# but they get truncated anyway so if more characters
@@ -616,6 +619,9 @@ class Activity(Window, gtk.Container):
# https://dev.laptop.org/ticket/3071
datastore.write(self._jobject)
+ # Tutorius: Now that everything is set, install the probe
+ self._probe = TProbe.TProbe(self.get_bundle_id(), self)
+
def get_active(self):
return self._active
diff --git a/toolkitfix/graphics/window.py b/toolkitfix/graphics/window.py
index 78996e0..b14a7c0 100644
--- a/toolkitfix/graphics/window.py
+++ b/toolkitfix/graphics/window.py
@@ -100,9 +100,10 @@ class Window(gtk.Window):
self._vbox.show()
- self._overlayer = Overlayer(self._vbox)
- self.add(self._overlayer)
- self._overlayer.show()
+ # Tutorius layer on which tutorials will be shown
+ self._overlayer = Overlayer(self._vbox)
+ self.add(self._overlayer)
+ self._overlayer.show()
self._is_fullscreen = False
self._unfullscreen_button = UnfullscreenButton()
diff --git a/tutorius/TProbe.py b/tutorius/TProbe.py
new file mode 100755
index 0000000..5280097
--- /dev/null
+++ b/tutorius/TProbe.py
@@ -0,0 +1,259 @@
+import gobject
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import cPickle as pickle
+
+import sugar.tutorius.addon as addon
+
+from sugar.tutorius.overlayer import Overlayer
+from sugar.tutorius.services import ObjectStore
+from sugar.tutorius.events import ClickedEvent
+
+import copy
+
+
+class TProbe(dbus.service.Object):
+
+ def __init__(self, activity_name, activity):
+
+ # Moving the ObjectStore assignment here, in the meantime
+ # the reference to the activity shouldn't be share as a
+ # global variable but passed by the Probe to the objects
+ # that requires it
+ self._activity = activity
+
+ ObjectStore().activity = activity
+
+ self._activity_name = activity_name
+ self._session_bus = dbus.SessionBus()
+
+ # Giving a new name because _name is already used by dbus
+ self._name2 = dbus.service.BusName(activity_name, self._session_bus)
+ dbus.service.Object.__init__(self, self._session_bus, "/tutorius/Probe")
+
+ # Add the dictionary we will use to store which actions and events
+ # are known
+ self._installedActions = {}
+ self._subscribedEvents = {}
+
+ # optional method to call if the probe is not inserted into an existing
+ # activity
+ def start(self):
+ mainloop = gobject.MainLoop()
+ print "Starting Probe for " + self._activity_name
+ mainloop.run()
+
+ @dbus.service.method("com.tutorius.ProbeInterface",
+ in_signature='s', out_signature='')
+ def registered(self, service):
+ print ("Registered with: " + str(service))
+
+ @dbus.service.method("com.tutorius.ProbeInterface",
+ in_signature='', out_signature='s')
+ def ping(self):
+ return "alive"
+
+ # ------------------ Action handling --------------------------------------
+ @dbus.service.method("com.tutorius.ProbeInterface",
+ in_signature='s', out_signature='s')
+ def install(self, pickled_action):
+ loaded_action = pickle.loads(str(pickled_action))
+ action = addon.create(loaded_action.__class__.__name__)
+
+ address = self._generate_action_reference(action)
+
+ self._installedActions[address] = action
+
+ if action._props:
+ action._props.update(loaded_action._props)
+
+ action.do()
+
+ return address
+
+ @dbus.service.method("com.tutorius.ProbeInterface",
+ in_signature='ss', out_signature='')
+ def update(self, address, action_props):
+ action = self._installedActions[address]
+
+ if action._props:
+ props = pickle.loads(str(action_props))
+ action._props.update(props)
+ action.undo()
+ action.do()
+
+ @dbus.service.method("com.tutorius.ProbeInterface",
+ in_signature='s', out_signature='')
+ def uninstall(self, address):
+ if self._installedActions.has_key(address):
+ action = self._installedActions[address]
+ action.undo()
+ self._installedActions.pop(address)
+
+
+ # ------------------ Event handling ---------------------------------------
+ @dbus.service.method("com.tutorius.ProbeInterface",
+ in_signature='s', out_signature='s')
+ def subscribe(self, pickled_event):
+ event = pickle.loads(str(pickled_event))
+
+ # TODO elavoie 2009-07-25 Move to a reference counting implementation
+ # to avoid duplicating eventfilters when the event signature is the
+ # same
+
+ # For now we will assume every probe is inserted in a GTK activity,
+ # however, in the future this should be moved in a subclass
+ eventfilter = addon.create("GtkWidgetEventFilter")
+
+ # There might be a validation of the Address in source in the future
+ # and a partial resolution to extract the object_id from the address
+ eventfilter.object_id = event.source
+
+ # TODO elavoie 2009-07-19
+ # There should be a type translation from a tutorius type
+ # to a GTK type here
+ eventfilter.event_name = event.type
+
+ # The callback uses the event defined previously and each
+ # successive call to subscribe will register a different
+ # callback that references a different event
+ def callback(*args):
+ self.notify(event)
+
+ eventfilter.install_handlers(callback, activity=self._activity)
+
+ name = self._generate_event_reference(event)
+ self._subscribedEvents[name] = eventfilter
+
+ return name
+
+ @dbus.service.method("com.tutorius.ProbeInterface",
+ in_signature='s', out_signature='')
+ def unsubscribe(self, address):
+ if self._subscribedEvents.has_key(address):
+ eventfilter = self._subscribedEvents[address]
+ eventfilter.remove_handlers()
+ self._subscribedEvents.pop(address)
+
+ @dbus.service.signal("com.tutorius.ProbeInterface")
+ def eventOccured(self, event):
+ # We need no processing now, the signal will be sent
+ # when the method exit
+ pass
+
+ # The actual method we will call on the probe to send events
+ def notify(self, event):
+ self.eventOccured(pickle.dumps(event))
+
+ # Return a unique name for this action
+ def _generate_action_reference(self, action):
+ # TODO elavoie 2009-07-25 Should return a universal address
+ name = action.__class__.__name__
+ suffix = 1
+
+ while self._installedActions.has_key(name+str(suffix)):
+ suffix += 1
+
+ return name + str(suffix)
+
+
+ # Return a unique name for this event
+ def _generate_event_reference(self, event):
+ # TODO elavoie 2009-07-25 Should return a universal address
+ name = event.type
+ suffix = 1
+
+ while self._subscribedEvents.has_key(name+str(suffix)):
+ suffix += 1
+
+ return name + str(suffix)
+
+
+
+class ProbeProxy:
+ def __init__(self, activityName):
+ bus = dbus.SessionBus()
+ self._object = bus.get_object(activityName, "/tutorius/Probe")
+ self._probe = dbus.Interface(self._object, "com.tutorius.ProbeInterface")
+
+ # We keep those two data structures to be able to have multiple callbacks
+ # for the same event and be able to remove them independently
+ self._subscribedEvents = {}
+ self._registeredCallbacks = {}
+
+ def _handle_signal(pickled_event):
+ event = pickle.loads(str(pickled_event))
+ if self._registeredCallbacks.has_key(event):
+ for callback in self._registeredCallbacks[event].itervalues():
+ callback(event)
+
+ self._object.connect_to_signal("eventOccured", _handle_signal, dbus_interface="com.tutorius.ProbeInterface")
+
+ def install(self, action):
+ address = str(self._probe.install(pickle.dumps(action)))
+ return address
+
+ def update(self, address, action):
+ self._probe.update(address, pickle.dumps(action._props))
+
+ def uninstall(self, address):
+ self._probe.uninstall(address)
+
+ def subscribe(self, event, callback):
+ # TODO elavoie 2009-07-25 When we will allow for patterns both
+ # for event types and sources, we will need to revise the lookup
+ # mecanism for which callback function to call
+
+
+ # Since multiple callbacks could be associated to the same
+ # event signature, we will store multiple callbacks
+ # in a dictionary indexed by the unique address
+ # given for this subscribtion and access this
+ # dictionary from another one indexed by event
+ address = str(self._probe.subscribe(pickle.dumps(event)))
+
+ # We use the event object as a key
+ if not self._registeredCallbacks.has_key(event):
+ self._registeredCallbacks[event] = {}
+
+ # TODO elavoie 2009-07-25 decide on a proper exception
+ # taxonomy
+ if self._registeredCallbacks[event].has_key(address):
+ # Oups, how come we have two similar addresses?
+ # send the bad news!
+ raise Exception("Probe subscribe exception, the following address already exists: " + str(address))
+
+ self._registeredCallbacks[event][address] = callback
+
+ # We will keep another dictionary to remember the
+ # event that was associated to this unique address
+ # Let's copy to make sure that even if the event
+ # passed in is modified later it won't screw up
+ # our dictionary (python pass arguments by reference)
+ self._subscribedEvents[address] = copy.copy(event)
+
+ return address
+
+ def unsubscribe(self, address):
+ self._probe.unsubscribe(address)
+
+ # Cleanup everything
+ if self._subscribedEvents.has_key(address):
+ event = self._subscribedEvents[address]
+
+ if self._registeredCallbacks.has_key(event)\
+ and self._registeredCallbacks[event].has_key(address):
+ self._registeredCallbacks[event].pop(address)
+
+ if self._registeredCallbacks[event] == {}:
+ self._registeredCallbacks.pop(event)
+
+ self._subscribedEvents.pop(address)
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+ probe = TProbe("com.tutorius.TBus")
+
+
diff --git a/tutorius/actions.py b/tutorius/actions.py
index 4269cd7..eb4a6b1 100644
--- a/tutorius/actions.py
+++ b/tutorius/actions.py
@@ -82,6 +82,7 @@ class DragWrapper(object):
"""Callback for end of drag (stolen focus)."""
self._dragging = False
+
def set_draggable(self, value):
"""Setter for the draggable property"""
if bool(value) ^ bool(self._drag_on):
@@ -177,6 +178,15 @@ class Action(TPropContainer):
self.position = [int(x), int(y)]
self.__edit_img.destroy()
+ # Adding methods for pickling and unpickling an object with
+ # properties
+ def __getstate__(self):
+ return self._props.copy()
+
+ def __setstate__(self, dict):
+ self._props.update(dict)
+
+
class OnceWrapper(Action):
"""
Wraps a class to perform an action once only
diff --git a/tutorius/editor_interpreter.py b/tutorius/editor_interpreter.py
new file mode 100644
index 0000000..d559266
--- /dev/null
+++ b/tutorius/editor_interpreter.py
@@ -0,0 +1,105 @@
+# Copyright (C) 2009, Tutorius.org
+# Greatly influenced by sugar/activity/namingalert.py
+#
+# 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
+""" Tutorial Editor Interpreter Module
+"""
+
+import gtk
+import pango
+from sugar.tutorius.ipython_view import *
+
+from gettext import gettext as _
+
+class EditorInterpreter(gtk.Window):
+ """
+ Interpreter that will be shown to the user
+ """
+ __gtype_name__ = 'TutoriusEditorInterpreter'
+
+ def __init__(self, activity=None):
+ gtk.Window.__init__(self)
+
+ self._activity = activity
+
+ # Set basic properties of window
+ self.set_decorated(False)
+ self.set_resizable(False)
+ self.set_modal(False)
+
+ # Connect to realize signal from ?
+ self.connect('realize', self.__realize_cb)
+
+ # Use an expander widget to allow minimizing
+ self._expander = gtk.Expander(_("Editor Interpreter"))
+ self._expander.set_expanded(True)
+ self.add(self._expander)
+ self._expander.connect("notify::expanded", self.__expander_cb)
+
+
+ # Use the IPython widget to embed
+ self.interpreter = IPythonView()
+ self.interpreter.set_wrap_mode(gtk.WRAP_CHAR)
+
+ # Expose the activity object in the interpreter
+ self.interpreter.updateNamespace({'activity':self._activity})
+
+ # Use a scroll window to permit scrolling of the interpreter prompt
+ swd = gtk.ScrolledWindow()
+ swd.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ swd.add(self.interpreter)
+ self.interpreter.show()
+
+ # Notify GTK that expander is ready
+ self._expander.add(swd)
+ self._expander.show()
+
+ # Notify GTK that the scrolling window is ready
+ swd.show()
+
+ def __expander_cb(self, *args):
+ """Callback for the window expander toggling"""
+ if self._expander.get_expanded():
+ self.__move_expanded()
+ else:
+ self.__move_collapsed()
+
+ def __move_expanded(self):
+ """Move the window to it's expanded position"""
+ swidth = gtk.gdk.screen_width()
+ sheight = gtk.gdk.screen_height()
+ # leave room for the scrollbar at the right
+ width = swidth - 20
+ height = 200
+
+ self.set_size_request(width, height)
+ # Put at the bottom of the screen
+ self.move(0, sheight-height)
+
+ def __move_collapsed(self):
+ """Move the window to it's collapsed position"""
+ width = 150
+ height = 40
+ swidth = gtk.gdk.screen_width()
+ sheight = gtk.gdk.screen_height()
+
+ self.set_size_request(width, height)
+ self.move(((swidth-width)/2)-150, sheight-height)
+
+ def __realize_cb(self, widget):
+ """Callback for realize"""
+ self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
+ self.window.set_accept_focus(True)
+ self.__move_expanded()
diff --git a/tutorius/events.py b/tutorius/events.py
new file mode 100644
index 0000000..bf0a8b9
--- /dev/null
+++ b/tutorius/events.py
@@ -0,0 +1,36 @@
+from sugar.tutorius.properties import *
+
+class Event(TPropContainer):
+ source = TUAMProperty()
+ type = TStringProperty("clicked")
+
+ def __init__(self):
+ TPropContainer.__init__(self)
+
+
+ # Providing the hash methods necessary to use events as key
+ # in a dictionary, if new properties are added we should
+ # take them into account here
+ def __hash__(self):
+ return hash(str(self.source) + str(self.type))
+
+ def __eq__(self, e2):
+ return self.source == e2.source and self.type == e2.type
+
+
+ # Adding methods for pickling and unpickling an object with
+ # properties
+ def __getstate__(self):
+ return self._props.copy()
+
+ def __setstate__(self, dict):
+ self._props.update(dict)
+
+
+# Nothing more needs to be added, the additional
+# information is in the object type
+class ClickedEvent(Event):
+ def __init__(self):
+ Event.__init__(self)
+ self.type = "clicked"
+
diff --git a/tutorius/ipython_view.py b/tutorius/ipython_view.py
new file mode 100644
index 0000000..c4294d0
--- /dev/null
+++ b/tutorius/ipython_view.py
@@ -0,0 +1,301 @@
+"""
+Backend to the console plugin.
+
+@author: Eitan Isaacson
+@organization: IBM Corporation
+@copyright: Copyright (c) 2007 IBM Corporation
+@license: BSD
+
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the BSD which accompanies this distribution, and
+is available at U{http://www.opensource.org/licenses/bsd-license.php}
+"""
+# this file is a modified version of source code from the Accerciser project
+# http://live.gnome.org/accerciser
+
+import gtk
+import re
+import sys
+import os
+import pango
+from StringIO import StringIO
+
+try:
+ import IPython
+except Exception,e:
+ raise "Error importing IPython (%s)" % str(e)
+
+ansi_colors = {'0;30': 'Black',
+ '0;31': 'Red',
+ '0;32': 'Green',
+ '0;33': 'Brown',
+ '0;34': 'Blue',
+ '0;35': 'Purple',
+ '0;36': 'Cyan',
+ '0;37': 'LightGray',
+ '1;30': 'DarkGray',
+ '1;31': 'DarkRed',
+ '1;32': 'SeaGreen',
+ '1;33': 'Yellow',
+ '1;34': 'LightBlue',
+ '1;35': 'MediumPurple',
+ '1;36': 'LightCyan',
+ '1;37': 'White'}
+
+class IterableIPShell:
+ def __init__(self,argv=None,user_ns=None,user_global_ns=None,
+ cin=None, cout=None,cerr=None, input_func=None):
+ if input_func:
+ IPython.iplib.raw_input_original = input_func
+ if cin:
+ IPython.Shell.Term.cin = cin
+ if cout:
+ IPython.Shell.Term.cout = cout
+ if cerr:
+ IPython.Shell.Term.cerr = cerr
+
+ if argv is None:
+ argv=[]
+
+ # This is to get rid of the blockage that occurs during
+ # IPython.Shell.InteractiveShell.user_setup()
+ IPython.iplib.raw_input = lambda x: None
+
+ self.term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
+ os.environ['TERM'] = 'dumb'
+ excepthook = sys.excepthook
+ self.IP = IPython.Shell.make_IPython(argv,user_ns=user_ns,
+ user_global_ns=user_global_ns,
+ embedded=True,
+ shell_class=IPython.Shell.InteractiveShell)
+ self.IP.system = lambda cmd: self.shell(self.IP.var_expand(cmd),
+ header='IPython system call: ',
+ verbose=self.IP.rc.system_verbose)
+ sys.excepthook = excepthook
+ self.iter_more = 0
+ self.history_level = 0
+ self.complete_sep = re.compile('[\s\{\}\[\]\(\)]')
+
+ def execute(self):
+ self.history_level = 0
+ orig_stdout = sys.stdout
+ sys.stdout = IPython.Shell.Term.cout
+ try:
+ line = self.IP.raw_input(None, self.iter_more)
+ if self.IP.autoindent:
+ self.IP.readline_startup_hook(None)
+ except KeyboardInterrupt:
+ self.IP.write('\nKeyboardInterrupt\n')
+ self.IP.resetbuffer()
+ # keep cache in sync with the prompt counter:
+ self.IP.outputcache.prompt_count -= 1
+
+ if self.IP.autoindent:
+ self.IP.indent_current_nsp = 0
+ self.iter_more = 0
+ except:
+ self.IP.showtraceback()
+ else:
+ self.iter_more = self.IP.push(line)
+ if (self.IP.SyntaxTB.last_syntax_error and
+ self.IP.rc.autoedit_syntax):
+ self.IP.edit_syntax_error()
+ if self.iter_more:
+ self.prompt = str(self.IP.outputcache.prompt2).strip()
+ if self.IP.autoindent:
+ self.IP.readline_startup_hook(self.IP.pre_readline)
+ else:
+ self.prompt = str(self.IP.outputcache.prompt1).strip()
+ sys.stdout = orig_stdout
+
+ def historyBack(self):
+ self.history_level -= 1
+ return self._getHistory()
+
+ def historyForward(self):
+ self.history_level += 1
+ return self._getHistory()
+
+ def _getHistory(self):
+ try:
+ rv = self.IP.user_ns['In'][self.history_level].strip('\n')
+ except IndexError:
+ self.history_level = 0
+ rv = ''
+ return rv
+
+ def updateNamespace(self, ns_dict):
+ self.IP.user_ns.update(ns_dict)
+
+ def complete(self, line):
+ split_line = self.complete_sep.split(line)
+ possibilities = self.IP.complete(split_line[-1])
+ if possibilities:
+ common_prefix = reduce(self._commonPrefix, possibilities)
+ completed = line[:-len(split_line[-1])]+common_prefix
+ else:
+ completed = line
+ return completed, possibilities
+
+ def _commonPrefix(self, str1, str2):
+ for i in range(len(str1)):
+ if not str2.startswith(str1[:i+1]):
+ return str1[:i]
+ return str1
+
+ def shell(self, cmd,verbose=0,debug=0,header=''):
+ stat = 0
+ if verbose or debug: print header+cmd
+ # flush stdout so we don't mangle python's buffering
+ if not debug:
+ input, output = os.popen4(cmd)
+ print output.read()
+ output.close()
+ input.close()
+
+class ConsoleView(gtk.TextView):
+ def __init__(self):
+ gtk.TextView.__init__(self)
+ self.modify_font(pango.FontDescription('Mono'))
+ self.set_cursor_visible(True)
+ self.text_buffer = self.get_buffer()
+ self.mark = self.text_buffer.create_mark('scroll_mark',
+ self.text_buffer.get_end_iter(),
+ False)
+ for code in ansi_colors:
+ self.text_buffer.create_tag(code,
+ foreground=ansi_colors[code],
+ weight=700)
+ self.text_buffer.create_tag('0')
+ self.text_buffer.create_tag('notouch', editable=False)
+ self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
+ self.line_start = \
+ self.text_buffer.create_mark('line_start',
+ self.text_buffer.get_end_iter(), True
+ )
+ self.connect('key-press-event', self._onKeypress)
+ self.last_cursor_pos = 0
+
+ def write(self, text, editable=False):
+ segments = self.color_pat.split(text)
+ segment = segments.pop(0)
+ start_mark = self.text_buffer.create_mark(None,
+ self.text_buffer.get_end_iter(),
+ True)
+ self.text_buffer.insert(self.text_buffer.get_end_iter(), segment)
+
+ if segments:
+ ansi_tags = self.color_pat.findall(text)
+ for tag in ansi_tags:
+ i = segments.index(tag)
+ self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(),
+ segments[i+1], tag)
+ segments.pop(i)
+ if not editable:
+ self.text_buffer.apply_tag_by_name('notouch',
+ self.text_buffer.get_iter_at_mark(start_mark),
+ self.text_buffer.get_end_iter())
+ self.text_buffer.delete_mark(start_mark)
+ self.scroll_mark_onscreen(self.mark)
+
+ def showPrompt(self, prompt):
+ self.write(prompt)
+ self.text_buffer.move_mark(self.line_start,self.text_buffer.get_end_iter())
+
+ def changeLine(self, text):
+ iter = self.text_buffer.get_iter_at_mark(self.line_start)
+ iter.forward_to_line_end()
+ self.text_buffer.delete(self.text_buffer.get_iter_at_mark(self.line_start), iter)
+ self.write(text, True)
+
+ def getCurrentLine(self):
+ rv = self.text_buffer.get_slice(self.text_buffer.get_iter_at_mark(self.line_start),
+ self.text_buffer.get_end_iter(), False)
+ return rv
+
+ def showReturned(self, text):
+ iter = self.text_buffer.get_iter_at_mark(self.line_start)
+ iter.forward_to_line_end()
+ self.text_buffer.apply_tag_by_name('notouch',
+ self.text_buffer.get_iter_at_mark(self.line_start),
+ iter)
+ self.write('\n'+text)
+ if text:
+ self.write('\n')
+ self.showPrompt(self.prompt)
+ self.text_buffer.move_mark(self.line_start,self.text_buffer.get_end_iter())
+ self.text_buffer.place_cursor(self.text_buffer.get_end_iter())
+
+ def _onKeypress(self, obj, event):
+ if not event.string:
+ return
+ insert_mark = self.text_buffer.get_insert()
+ insert_iter = self.text_buffer.get_iter_at_mark(insert_mark)
+ selection_mark = self.text_buffer.get_selection_bound()
+ selection_iter = self.text_buffer.get_iter_at_mark(selection_mark)
+ start_iter = self.text_buffer.get_iter_at_mark(self.line_start)
+ if start_iter.compare(insert_iter) <= 0 and \
+ start_iter.compare(selection_iter) <= 0:
+ return
+ elif start_iter.compare(insert_iter) > 0 and \
+ start_iter.compare(selection_iter) > 0:
+ self.text_buffer.place_cursor(start_iter)
+ elif insert_iter.compare(selection_iter) < 0:
+ self.text_buffer.move_mark(insert_mark, start_iter)
+ elif insert_iter.compare(selection_iter) > 0:
+ self.text_buffer.move_mark(selection_mark, start_iter)
+
+
+class IPythonView(ConsoleView, IterableIPShell):
+ def __init__(self):
+ ConsoleView.__init__(self)
+ self.cout = StringIO()
+ IterableIPShell.__init__(self, cout=self.cout,cerr=self.cout,
+ input_func=self.raw_input)
+ self.connect('key_press_event', self.keyPress)
+ self.execute()
+ self.cout.truncate(0)
+ self.showPrompt(self.prompt)
+ self.interrupt = False
+
+ def raw_input(self, prompt=''):
+ if self.interrupt:
+ self.interrupt = False
+ raise KeyboardInterrupt
+ return self.getCurrentLine()
+
+ def keyPress(self, widget, event):
+ if event.state & gtk.gdk.CONTROL_MASK and event.keyval == 99:
+ self.interrupt = True
+ self._processLine()
+ return True
+ elif event.keyval == gtk.keysyms.Return:
+ self._processLine()
+ return True
+ elif event.keyval == gtk.keysyms.Up:
+ self.changeLine(self.historyBack())
+ return True
+ elif event.keyval == gtk.keysyms.Down:
+ self.changeLine(self.historyForward())
+ return True
+ elif event.keyval == gtk.keysyms.Tab:
+ if not self.getCurrentLine().strip():
+ return False
+ completed, possibilities = self.complete(self.getCurrentLine())
+ if len(possibilities) > 1:
+ slice = self.getCurrentLine()
+ self.write('\n')
+ for symbol in possibilities:
+ self.write(symbol+'\n')
+ self.showPrompt(self.prompt)
+ self.changeLine(completed or slice)
+ return True
+
+ def _processLine(self):
+ self.history_pos = 0
+ self.execute()
+ rv = self.cout.getvalue()
+ if rv: rv = rv.strip('\n')
+ self.showReturned(rv)
+ self.cout.truncate(0)
+
diff --git a/tutorius/properties.py b/tutorius/properties.py
index 34b508a..6d47688 100644
--- a/tutorius/properties.py
+++ b/tutorius/properties.py
@@ -287,7 +287,12 @@ class TUAMProperty(TutoriusProperty):
Represents a widget of the interface by storing its UAM.
"""
# TODO : Pending UAM check-in (LP 355199)
- pass
+ def __init__(self, value=None):
+ TutoriusProperty.__init__(self)
+
+ self.type = "uam"
+
+
class TAddonProperty(TutoriusProperty):
"""