Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/J2JToolbars.py
diff options
context:
space:
mode:
Diffstat (limited to 'J2JToolbars.py')
-rwxr-xr-xJ2JToolbars.py621
1 files changed, 621 insertions, 0 deletions
diff --git a/J2JToolbars.py b/J2JToolbars.py
new file mode 100755
index 0000000..f5a739f
--- /dev/null
+++ b/J2JToolbars.py
@@ -0,0 +1,621 @@
+#This python module is part of the Jam2Jam XO Activity, March, 2010
+#
+#Copyright (C) 2010 Thorin Kerr & Andrew Brown
+#
+#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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import sugar.activity
+from sugar.activity.activity import get_bundle_path
+from olpcgames import activity
+import gtk, gst, thread, tempfile, time, os, types
+import pygame, olpcgames
+from gettext import gettext as _
+
+_NEW_TOOLBAR_SUPPORT = True
+
+from sugar.graphics.toolbutton import ToolButton #used in both old and new toolbars, but I suspect the new toolbar could use ToolbarButton instead.
+
+try:
+ from sugar.graphics.toolbarbox import ToolbarBox #new toolbar
+ from sugar.graphics.toolbarbox import ToolbarButton #new toolbar
+ from sugar.activity.widgets import StopButton #new toolbar
+ from mybutton import AltButton #new toolbar
+except:
+ _NEW_TOOLBAR_SUPPORT = True
+ from sugar.graphics.toolcombobox import ToolComboBox #only needs to be imported if using old toolbar
+
+import logging
+log = logging.getLogger('City run')
+log.setLevel(logging.DEBUG)
+log.info("imported AltButton, inherits from ToolbarButton\n")
+
+ImagePath = get_bundle_path() + "/City/Images"
+
+GST_PIPE = ['v4l2src', 'ffmpegcolorspace', 'pngenc']
+
+class readScenes(object):
+ def __init__(self, scpath):
+ self.scpath = scpath
+ self.scene_names = [dir for dir in os.listdir(self.scpath) if os.path.isdir(self.scpath + '/' + dir)]
+ self.scene_data = []
+ for n in self.scene_names:
+ fp = self.scpath + "/" + n
+ mdfile = [open(fp + '/' + f) for f in os.listdir(fp) if os.path.isfile(fp + "/" + f) and f.startswith(n)]
+ if mdfile:
+ result = {"Name":n}
+ defaults = {}
+ for line in mdfile[0]:
+ if line.startswith('#') or line.startswith('\n'):
+ pass
+ else:
+ keyvals = line.split('=')
+ if len(keyvals) == 2:
+ key = keyvals[0].upper()
+ val = (keyvals[1][:-1] if keyvals[1][-1] == '\n' else keyvals[1])
+ if key.startswith('TEMPO'):
+ result['Tempo'] = val.replace(' ','')
+ elif key.startswith('KEY'):
+ result['Key'] = val.replace(' ','')
+ elif key.startswith('MODE'):
+ result['Mode'] = val.replace(' ','')
+ else:
+ pass
+ else:
+ raise IOError, "Bad Scene Meta Data file: %s" %keyvals
+ result['Defaults'] = {}
+ self.scene_data.append(result)
+ else:
+ raise IOError, "Can't find Meta Data file in %s Scene" %n
+ def scene_instruct(self, name):
+ "returns a list of strings suitable to give to a ScenePlayer object for creating a scene"
+ for scd in self.scene_data:
+ if scd['Name'] == name:
+ collected = [name]
+ for k in ['Key', 'Mode', 'Tempo', 'Defaults']:
+ try:
+ collected.append(str(scd[k]))
+ except KeyError:
+ collected.append('None')
+ return collected
+ def get_scene_list(self):
+ "returns a list of scene strings for the toolbar, with City as the default"
+ ordered_names = self.scene_names[:]
+ if 'City' in ordered_names:
+ ordered_names.insert(0,ordered_names.pop(ordered_names.index('City')))
+ return [self.scene_instruct(s) for s in ordered_names]
+
+class CameraSnap(object):
+ """A class representing the OLPC camera."""
+ def __init__(self):
+ log.info("CameraSnap init")
+ snap_file, self.snap_path = tempfile.mkstemp(suffix = '.png')
+ pipe = GST_PIPE + ['filesink location=%s' % self.snap_path]
+ self.pipe = gst.parse_launch('!'.join(pipe))
+ self.bus = self.pipe.get_bus()
+ log.info("tempfile is %s " %self.snap_path)
+ def Snap(self):
+ """Take a snapshot."""
+ log.info("about to set pipe state to PLAY")
+ self.pipe.set_state(gst.STATE_PLAYING)
+ log.info("about to poll")
+ thread.start_new_thread(self.bus.poll, (gst.MESSAGE_EOS, -1))
+ for i in xrange(60):
+ time.sleep(0.18)
+ if os.path.getsize(self.snap_path) > 0: break
+ else: raise IOError, "Error writing camera snap to file"
+ return self.snap_path
+ def Stop(self):
+ self.pipe.set_state(gst.STATE_NULL)
+
+#old toolbar
+class Jam2JamToolBar(gtk.Toolbar):
+ def __init__(self, activity):
+ gtk.Toolbar.__init__(self)
+ self.activity = activity
+ self.parameters = ['Density', 'Pitch', 'Length', 'Timbre', 'Volume'] # no tempo here.
+ scene_stuff = readScenes(self.activity._ScenePath)
+ self.scenes = scene_stuff.get_scene_list()
+ #example of scene data. This gets read from file
+ #self.scenes = [['City', 'A', 'minor pentatonic'], ['City', 'G#', 'major']] #this data needs to be obtained from directories
+ self.play_pause_state = 'Playing'
+ self.scene_init = True
+
+ # Separator
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ self.insert(separator, -1)
+
+ #Horizontal Parameter control combobox
+ self._add_widget(gtk.Label(_('Horizontal:')))
+ self._Hparameter_combo = ToolComboBox()
+ for i, f in enumerate(self.parameters):
+ self._Hparameter_combo.combo.append_item(i, f)
+ self._Hparameter_combo.combo.connect('changed', self._Hparameter_change_cb)
+ self._add_widget(self._Hparameter_combo)
+ self._Hparameter_combo.combo.set_active(0)
+
+ # Separator
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+
+ #Vertical Parameter control combobox
+ self._add_widget(gtk.Label(_('Vertical:')))
+ self._Vparameter_combo = ToolComboBox()
+ for j, k in enumerate(self.parameters):
+ self._Vparameter_combo.combo.append_item(j, k)
+ self._Vparameter_combo.combo.connect('changed', self._Vparameter_change_cb)
+ self._add_widget(self._Vparameter_combo)
+ self._Vparameter_combo.combo.set_active(1)
+
+ # Separator
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+
+ #Scene Selection control combobox
+ self._add_widget(gtk.Label(_('Scene:')))
+ self._Scene_combo = ToolComboBox()
+ for l, m in enumerate(self.scenes):
+ self._Scene_combo.combo.append_item(l, m[0])
+ self._Scene_combo.combo.connect('changed', self._Scene_change_cb)
+ self._add_widget(self._Scene_combo)
+ #ought to do this safely somehow.
+ self._Scene_combo.combo.set_active(0)
+ self.scene_init = False
+
+ # Separator
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+
+ #Camera Button
+ self.camera_ready = True
+ camera_icon = ImagePath + "/camera-external.svg"
+ camera_busy_icon = ImagePath + "/camera-busy.svg"
+ self.camera_image, self.camera_busy_image = gtk.Image(), gtk.Image()
+ self.camera_image.set_from_file(camera_icon)
+ self.camera_busy_image.set_from_file(camera_busy_icon)
+ self.camera_image.show()
+ #camera_busy_image.show()
+ self._cameraButton = ToolButton()
+ self._cameraButton.set_icon_widget(self.camera_image)
+ self._cameraButton.connect('clicked', self._cameraSnap_cb)
+ self._cameraButton.set_tooltip(_('Snapshot'))
+ self.insert(self._cameraButton, -1)
+ self._cameraButton.show()
+
+ # Separator
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+
+ #Play/Pause Button
+ pause_icon = ImagePath + "/media-playback-pause.svg"
+ play_icon = ImagePath + "/media-playback-start.svg"
+ self.pause_image = gtk.Image()
+ self.pause_image.set_from_file(pause_icon)
+
+ self.play_image = gtk.Image()
+ self.play_image.set_from_file(play_icon)
+
+ self._pauseButton = ToolButton()
+ self._pauseButton.connect('clicked', self._pause_cb)
+ self.pause_image.show()
+ self._pauseButton.set_icon_widget(self.pause_image)
+ self._pauseButton.set_tooltip(_('Pause'))
+ #self._toggleplay_pause()
+ self.insert(self._pauseButton, -1)
+ self._pauseButton.show()
+
+ # Separator
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+
+
+ def _add_widget(self, widget, expand=False):
+ tool_item = gtk.ToolItem()
+ tool_item.set_expand(expand)
+ tool_item.add(widget)
+ widget.show()
+ self.insert(tool_item, -1)
+ tool_item.show()
+
+ def _toggleplay_pause(self):
+ if self.play_pause_state == "Playing":
+ self.activity.jamScene.music_player.pause()
+ self.play_image.show()
+ self._pauseButton.set_icon_widget(self.play_image)
+ self._pauseButton.set_tooltip(_('Play'))
+ self.play_pause_state = "Paused"
+ else:
+ self.activity.jamScene.music_player.resume()
+ self.pause_image.show()
+ self._pauseButton.set_icon_widget(self.pause_image)
+ self._pauseButton.set_tooltip(_('Pause'))
+ self.play_pause_state = "Playing"
+ try:
+ self.activity._pgc.grab_focus()
+ except AttributeError:
+ pass
+
+ def _show_busy_camera(self):
+ self.camera_ready = False
+ self.camera_busy_image.show()
+ self._cameraButton.set_icon_widget(self.camera_busy_image)
+ self._cameraButton.set_tooltip(_('Please wait...'))
+
+ def _show_active_camera(self):
+ self.camera_image.show()
+ self._cameraButton.set_icon_widget(self.camera_image)
+ self._cameraButton.set_tooltip(_('Snap'))
+ self.camera_ready = True
+
+ def _Hparameter_change_cb(self, widget):
+ param = "Parameter|Horizontal|" + self.parameters[self._Hparameter_combo.combo.get_active()]
+ olpcgames.eventwrap.post(olpcgames.eventwrap.Event(pygame.USEREVENT, action=param))
+ try:
+ self.activity._pgc.grab_focus()
+ except AttributeError:
+ pass
+
+ def _Vparameter_change_cb(self, widget):
+ param = "Parameter|Vertical|" + self.parameters[self._Vparameter_combo.combo.get_active()]
+ olpcgames.eventwrap.post(olpcgames.eventwrap.Event(pygame.USEREVENT, action=param))
+ try:
+ self.activity._pgc.grab_focus()
+ except AttributeError:
+ pass
+
+ def _Scene_change_cb(self, widget):
+ if self.scene_init:
+ pass
+ else:
+ selection = self.scenes[self._Scene_combo.combo.get_active()]
+ scene = "Reload|" + '|'.join(map(lambda x: str(x), selection))
+ olpcgames.eventwrap.post(olpcgames.eventwrap.Event(pygame.USEREVENT, action=scene))
+ try:
+ self.activity._pgc.grab_focus()
+ except AttributeError:
+ pass
+
+ ### functions to assist calls from pygame
+ def deactivate_scene_change(self):
+ self._Scene_combo.set_sensitive(False)
+ def reactivate_scene_change(self):
+ self._Scene_combo.set_sensitive(True)
+ def set_horizontal_parameter(self, param):
+ ndx = self.parameters.index(param)
+ self._Hparameter_combo.combo.set_active(ndx)
+ def set_vertical_parameter(self, param):
+ ndx = self.parameters.index(param)
+ self._Vparameter_combo.combo.set_active(ndx)
+
+ def _cameraSnap_cb(self, widget):
+ "Here I could wrap a camera event..."
+ def snaptime():
+ snap = CameraSnap()
+ self.activity.cameras_loaded.append(snap)
+ picpath = snap.Snap()
+ self.activity.load_image(picpath)
+ snap.Stop()
+ self._show_active_camera()
+ self.activity._pgc.grab_focus()
+ if self.camera_ready:
+ self._show_busy_camera()
+ thread.start_new_thread(snaptime, ())
+ else:
+ log.info('Ignoring request to use camera, as camera is currently busy')
+
+ def _pause_cb(self, widget):
+ self._toggleplay_pause()
+ log.info("Play/Pause Button pressed")
+
+
+class J2J_Toolbar_Redirect( object ):
+ "This object provides an API interface for pygame calls to the new toolbars"
+ def __init__(self, activity_toolbar, horizontal_toolbar, vertical_toolbar, scene_toolbar):
+ self.horizontal_toolbar = horizontal_toolbar
+ self.vertical_toolbar = vertical_toolbar
+ self.activity_toolbar = activity_toolbar
+ def deactivate_scene_change(self):
+ "The music style menu gets frozen when jamming on a network"
+ stb = self.activity_toolbar.nameID['Music']
+ stb.set_sensitive(False)
+ def reactivate_scene_change(self):
+ stb = self.activity_toolbar.nameID['Music']
+ stb.set_sensitive(True)
+ def set_horizontal_parameter(self, param):
+ log.info("set_horizontal_parameter received %s" %param)
+ self.horizontal_toolbar.set_horizontal_parameter(param)
+ def set_vertical_parameter(self, param):
+ log.info("set_vertical_parameter received %s" %param)
+ self.vertical_toolbar.set_vertical_parameter(param)
+
+
+#should probably tidy these toolbars up. Subclass them.
+class Jam2JamActivityToolbar( gtk.Toolbar ):
+ def __init__(self, activity):
+ log.info('entering __init__ in Jam2JamActivityToolbar')
+ self.toolbar_box = ToolbarBox()
+ log.info('made the activity toolbar_box')
+ self.activity = activity
+ activity_button = AltButton(activity)
+ log.info ('made the ALT BUTTON')
+ self.toolbar_box.toolbar.insert(activity_button, 0)
+ activity_button.show()
+ self.nameID = {}
+
+ def addCallback(self, func):
+ "used to add a callback function to buttons, make sure you do this before trying to add the button"
+ result = types.MethodType(func, self)
+ self.__setattr__(func.__name__, result)
+
+ def addToolBarButton(self, nameID, icon_name, toolbar=None):
+ "I wonder if this can be merged with the ad button below?"
+ button = ToolbarButton(
+ page=toolbar,
+ icon_name=icon_name)
+ self.toolbar_box.toolbar.insert(button, -1)
+ button.show()
+ toolbar.show()
+ self.nameID.update({nameID:button})
+
+ def addSeparator(self):
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.toolbar_box.toolbar.insert(separator, -1)
+
+ def addButton(self, nameID, image1path, image2path, tooltip1, tooltip2, cb):
+ button = ToolButton()
+ log.info("addButtonIMAGE PATH ____ " + image1path )
+ button.image1 = gtk.Image()
+ button.image1.set_from_file(image1path)
+ button.image2 = gtk.Image()
+ button.image2.set_from_file(image2path)
+ button.image1.show()
+ button.tooltip1, button.tooltip2 = tooltip1, tooltip2
+ button.set_icon_widget(button.image1)
+ button.connect('clicked', cb)
+ button.set_tooltip(_(button.tooltip1))
+ button.nameID = nameID
+ self.toolbar_box.toolbar.insert(button, -1)
+ button.show()
+ self.nameID.update({nameID:button})
+
+ def addStopButton(self):
+ separator = gtk.SeparatorToolItem()
+ separator.props.draw = False
+ separator.set_expand(True)
+ separator.show()
+ self.toolbar_box.toolbar.insert(separator, -1)
+ stop_button = StopButton(self.activity)
+ stop_button.props.accelerator = '<Ctrl><Shift>Q'
+ self.toolbar_box.toolbar.insert(stop_button, -1)
+ stop_button.show()
+
+ def getToolItem(self, nameID):
+ return self.nameID[nameID]
+
+ def play_pause_cb(self, button):
+ if self.activity.play_pause_state == "Playing":
+ self.activity.jamScene.music_player.pause()
+ button.image2.show()
+ button.set_icon_widget(button.image2)
+ button.set_tooltip(_('Play'))
+ self.activity.play_pause_state = "Paused"
+ else:
+ self.activity.jamScene.music_player.resume()
+ button.image1.show()
+ button.set_icon_widget(button.image1)
+ button.set_tooltip(_('Pause'))
+ self.activity.play_pause_state = "Playing"
+ try:
+ self.activity._pgc.grab_focus()
+ except AttributeError:
+ pass
+
+ def camera_cb(self, button):
+ "Here I could wrap a camera event..."
+ if not hasattr(self, 'camera_ready'): self.camera_ready = True
+ def show_busy_camera(button):
+ self.camera_ready = False
+ button.image2.show()
+ button.set_icon_widget(button.image2)
+ button.set_tooltip(_('Please wait...'))
+ def show_active_camera(button):
+ button.image1.show()
+ button.set_icon_widget(button.image1)
+ button.set_tooltip(_('Snap'))
+ self.camera_ready = True
+ def snaptime(button):
+ snap = CameraSnap()
+ self.activity.cameras_loaded.append(snap)
+ picpath = snap.Snap()
+ self.activity.load_image(picpath)
+ snap.Stop()
+ show_active_camera(button)
+ self.activity._pgc.grab_focus()
+ if self.camera_ready:
+ show_busy_camera(button)
+ thread.start_new_thread(snaptime, (button,))
+ else:
+ log.info('Ignoring request to use camera, as camera is currently busy')
+
+
+class Jam2JamParameterToolbar(gtk.Toolbar):
+ def __init__(self, activity):
+ gtk.Toolbar.__init__(self)
+ self.activity = activity
+ self.parameters = ['Pitch', 'Timbre', 'Density', 'Volume', 'Length'] # no tempo here yet.
+ self.scene_init = True
+ self.activeButton = None
+
+ def addButton(self, nameID, image1path, image2path, tooltip1, tooltip2, cb):
+ button = ToolButton()
+ button.image1 = gtk.Image()
+ button.image1.set_from_file(image1path)
+ button.image2 = gtk.Image()
+ button.image2.set_from_file(image2path)
+ button.image1.show()
+ button.nameID = nameID
+ button.tooltip1, button.tooltip2 = tooltip1, tooltip2
+ button.set_icon_widget(button.image1)
+ button.connect('clicked', cb)
+ button.set_tooltip(_(button.tooltip1))
+ button.active = False
+ self.insert(button, -1)
+ button.show()
+ return button
+
+ def toggleButtonIcon(self, button):
+ if button.active:
+ button.image1.show()
+ button.set_icon_widget(button.image1)
+ button.set_tooltip(_(button.tooltip1))
+ button.active = False
+ else:
+ button.image2.show()
+ button.set_icon_widget(button.image2)
+ button.set_tooltip(_(button.tooltip2))
+ button.active = True
+
+
+ def addSeparator(self):
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+
+ def set_horizontal_parameter(self, param):
+ buttons = self.get_children()
+ for b in buttons:
+ if b.nameID == param:
+ self.set_horizontal_cb(b)
+ break
+ else:
+ raise TypeError('%s keystroke is not a recognised parameter' %param)
+
+ def set_horizontal_cb(self, button):
+ log.info('button pressed - %s' %button.nameID)
+ if self.activeButton: self.toggleButtonIcon(self.activeButton)
+ self.activeButton = button
+ self.toggleButtonIcon(self.activeButton)
+ param = "Parameter|Horizontal|" + button.nameID
+ olpcgames.eventwrap.post(olpcgames.eventwrap.Event(pygame.USEREVENT, action=param))
+ try:
+ self.activity._pgc.grab_focus()
+ except AttributeError:
+ pass
+
+ def set_vertical_parameter(self, param):
+ buttons = self.get_children()
+ for b in buttons:
+ if b.nameID == param:
+ self.set_vertical_cb(b)
+ break
+ else:
+ raise TypeError('%s keystroke is not a recognised parameter' %param)
+
+ def set_vertical_cb(self, button):
+ log.info('button pressed - %s' %button.nameID)
+ if self.activeButton: self.toggleButtonIcon(self.activeButton)
+ self.activeButton = button
+ self.toggleButtonIcon(self.activeButton)
+ param = "Parameter|Vertical|" + button.nameID
+ olpcgames.eventwrap.post(olpcgames.eventwrap.Event(pygame.USEREVENT, action=param))
+ try:
+ self.activity._pgc.grab_focus()
+ except AttributeError:
+ pass
+
+
+class Jam2JamSceneToolbar(gtk.Toolbar):
+ "Toolbar which sets musicl styles"
+ def __init__(self, activity):
+ gtk.Toolbar.__init__(self)
+ self.activity = activity
+ scene_stuff = readScenes(self.activity._ScenePath)
+ self.scenes = scene_stuff.get_scene_list()
+ log.info("SCENE DATA IS %s" %self.scenes)
+ #format of self.scenes
+ #self.scenes = [['City', 'A', 'minor pentatonic'], ['City', 'G#', 'major']] #this data is read from file
+ self.play_pause_state = 'Playing'
+ self.activeButton = None
+ #self.scene_init = True
+
+ def addButton(self, nameID, image1path, image2path, tooltip1, tooltip2, cb):
+ button = ToolButton()
+ button.image1 = gtk.Image()
+ button.image1.set_from_file(image1path)
+ button.image2 = gtk.Image()
+ button.image2.set_from_file(image2path)
+ button.image1.show()
+ button.nameID = nameID
+ button.tooltip1, button.tooltip2 = tooltip1, tooltip2
+ button.set_icon_widget(button.image1)
+ button.connect('clicked', cb)
+ button.set_tooltip(_(button.tooltip1))
+ button.active = False
+ self.insert(button, -1)
+ button.show()
+ return button
+
+ def toggleButtonIcon(self, button):
+ if button.active:
+ button.image1.show()
+ button.set_icon_widget(button.image1)
+ button.set_tooltip(_(button.tooltip1))
+ button.active = False
+ else:
+ button.image2.show()
+ button.set_icon_widget(button.image2)
+ button.set_tooltip(_(button.tooltip2))
+ button.active = True
+
+ def _get_scene_data(self, button):
+ button_name = button.nameID
+ for item in self.scenes:
+ if item[0] == button_name: return item
+ else:
+ raise IOError('could not match button name %s to scene data %s' %(button_name, self.scenes))
+
+ def scene_change_cb(self, button):
+ log.info("scene change requested: %s" %button.nameID)
+ if self.activeButton: self.toggleButtonIcon(self.activeButton)
+ self.activeButton = button
+ self.toggleButtonIcon(self.activeButton)
+ selection = self._get_scene_data(button)
+ #Scene message should look like this: "Reload|name|key:mode|tempo|defaults"
+ scene = "Reload|" + '|'.join(map(lambda x: str(x), selection))
+ olpcgames.eventwrap.post(olpcgames.eventwrap.Event(pygame.USEREVENT, action=scene))
+ try:
+ self.activity._pgc.grab_focus()
+ except AttributeError:
+ pass
+
+ def addSeparator(self):
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+