Allow to run activities without Sugar shell.
- Handle lack of GSettings gracefully. - Still requires sugar-datastore. - Supports avoiding X11 docks/panels. - Provides icons for Activity windows. Try it outside Sugar. Go to an Activity directory and run 'sugar-activity'. Tested it with Terminal, Finance, Write, Browse, Memorize under XFCE4. Tested in Gnome under OLPC-OS. Also works from Sugar Terminal Activity. Does not affect regular Sugar operation. This is patch v.2 - Addresses most concerns: - Removed commented code, sorry. - Changed code to use profile.get_nickname and get_color where possible. Couldn't the launcher just pass this info? Maybe the launcher could set the activity root as well? - It is intended to be usable from the command line also. Should put sugar version in the environment - It is intended to work even without Sugar Shell installed. Why don't we always set the icon? - On XO it might use some memory. I was concerned to degrade performance. Also, imports should be at the top of the file? - Also a concern about performance on XO. This way it is only loaded in this use case. Maybe it is insignificant -moved as requested. It would be nice if the changes to the POT for sugar-toolkit-gtk3 could be incorporated in this pull request, please. - There were no changes to POT files as part of this patch. Maybe it is worth translating low level command line tools, not sure. Suggest packaged activities might also provide .desktop files. - Intriguing but not sure within scope of this patch. You mean generate a .desktop file automatically as an option? Sounds nice! Suggest sugar-activity might also accept path to unpacked bundle. - Implemented!
This commit is contained in:
parent
ab0f5a18aa
commit
2f2b2d20d8
45
bin/sugar-activity
Normal file → Executable file
45
bin/sugar-activity
Normal file → Executable file
@ -35,8 +35,10 @@ DBusGMainLoop(set_as_default=True)
|
|||||||
from sugar3.activity import activityhandle
|
from sugar3.activity import activityhandle
|
||||||
from sugar3 import config
|
from sugar3 import config
|
||||||
from sugar3.bundle.activitybundle import ActivityBundle
|
from sugar3.bundle.activitybundle import ActivityBundle
|
||||||
|
from sugar3.activity.activityfactory import get_environment
|
||||||
from sugar3 import logger
|
from sugar3 import logger
|
||||||
|
|
||||||
|
from sugar3.bundle.bundle import MalformedBundleException
|
||||||
|
|
||||||
def create_activity_instance(constructor, handle):
|
def create_activity_instance(constructor, handle):
|
||||||
activity = constructor(handle)
|
activity = constructor(handle)
|
||||||
@ -69,7 +71,13 @@ class SingleProcess(dbus.service.Object):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = OptionParser()
|
usage = 'usage: %prog [options] [activity dir] [python class]'
|
||||||
|
epilog = 'If you are running from a directory containing an Activity, ' \
|
||||||
|
'the argument may be ommitted. Otherwise please provide either '\
|
||||||
|
'a directory containing a Sugar Activity [activity dir], a '\
|
||||||
|
'[python_class], or both.'
|
||||||
|
|
||||||
|
parser = OptionParser(usage=usage, epilog=epilog)
|
||||||
parser.add_option('-b', '--bundle-id', dest='bundle_id',
|
parser.add_option('-b', '--bundle-id', dest='bundle_id',
|
||||||
help='identifier of the activity bundle')
|
help='identifier of the activity bundle')
|
||||||
parser.add_option('-a', '--activity-id', dest='activity_id',
|
parser.add_option('-a', '--activity-id', dest='activity_id',
|
||||||
@ -89,18 +97,34 @@ def main():
|
|||||||
|
|
||||||
logger.start()
|
logger.start()
|
||||||
|
|
||||||
if 'SUGAR_BUNDLE_PATH' not in os.environ:
|
activity_class = None
|
||||||
print 'SUGAR_BUNDLE_PATH is not defined in the environment.'
|
if len(args) == 2:
|
||||||
sys.exit(1)
|
activity_class = args[1]
|
||||||
|
os.chdir(args[0])
|
||||||
|
elif len(args) == 1:
|
||||||
|
if os.path.isdir(args[0]):
|
||||||
|
os.chdir(args[0])
|
||||||
|
else:
|
||||||
|
activity_class = args[0]
|
||||||
|
|
||||||
if len(args) == 0:
|
os.environ['SUGAR_BUNDLE_PATH'] = os.path.abspath(os.curdir)
|
||||||
print 'A python class must be specified as first argument.'
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
bundle_path = os.environ['SUGAR_BUNDLE_PATH']
|
bundle_path = os.environ['SUGAR_BUNDLE_PATH']
|
||||||
sys.path.append(bundle_path)
|
sys.path.append(bundle_path)
|
||||||
|
|
||||||
|
try:
|
||||||
bundle = ActivityBundle(bundle_path)
|
bundle = ActivityBundle(bundle_path)
|
||||||
|
except MalformedBundleException:
|
||||||
|
parser.print_help()
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
if not activity_class:
|
||||||
|
if bundle.get_command().startswith('sugar-activity'):
|
||||||
|
print 'Guessing python class from activity.info!'
|
||||||
|
activity_class = bundle.get_command().split(" ")[1]
|
||||||
|
|
||||||
|
if 'SUGAR_VERSION' not in os.environ:
|
||||||
|
environ = get_environment(bundle)
|
||||||
|
|
||||||
os.environ['SUGAR_BUNDLE_ID'] = bundle.get_bundle_id()
|
os.environ['SUGAR_BUNDLE_ID'] = bundle.get_bundle_id()
|
||||||
os.environ['SUGAR_BUNDLE_NAME'] = bundle.get_name()
|
os.environ['SUGAR_BUNDLE_NAME'] = bundle.get_name()
|
||||||
@ -114,7 +138,7 @@ def main():
|
|||||||
gettext.bindtextdomain('sugar-toolkit-gtk3', config.locale_path)
|
gettext.bindtextdomain('sugar-toolkit-gtk3', config.locale_path)
|
||||||
gettext.textdomain(bundle.get_bundle_id())
|
gettext.textdomain(bundle.get_bundle_id())
|
||||||
|
|
||||||
splitted_module = args[0].rsplit('.', 1)
|
splitted_module = activity_class.rsplit('.', 1)
|
||||||
module_name = splitted_module[0]
|
module_name = splitted_module[0]
|
||||||
class_name = splitted_module[1]
|
class_name = splitted_module[1]
|
||||||
|
|
||||||
@ -123,6 +147,11 @@ def main():
|
|||||||
module = getattr(module, comp)
|
module = getattr(module, comp)
|
||||||
|
|
||||||
activity_constructor = getattr(module, class_name)
|
activity_constructor = getattr(module, class_name)
|
||||||
|
|
||||||
|
if not options.activity_id:
|
||||||
|
options.activity_id = bundle.get_name()
|
||||||
|
options.bundle_id = bundle.get_bundle_id()
|
||||||
|
|
||||||
activity_handle = activityhandle.ActivityHandle(
|
activity_handle = activityhandle.ActivityHandle(
|
||||||
activity_id=options.activity_id,
|
activity_id=options.activity_id,
|
||||||
object_id=options.object_id, uri=options.uri,
|
object_id=options.object_id, uri=options.uri,
|
||||||
|
@ -67,7 +67,6 @@ import json
|
|||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
from gi.repository import Gdk
|
from gi.repository import Gdk
|
||||||
from gi.repository import GObject
|
from gi.repository import GObject
|
||||||
from gi.repository import Gio
|
|
||||||
import dbus
|
import dbus
|
||||||
import dbus.service
|
import dbus.service
|
||||||
from dbus import PROPERTIES_IFACE
|
from dbus import PROPERTIES_IFACE
|
||||||
@ -81,6 +80,7 @@ from telepathy.constants import CONNECTION_HANDLE_TYPE_ROOM
|
|||||||
|
|
||||||
from sugar3 import util
|
from sugar3 import util
|
||||||
from sugar3 import power
|
from sugar3 import power
|
||||||
|
from sugar3.profile import get_nick_name, get_color
|
||||||
from sugar3.presence import presenceservice
|
from sugar3.presence import presenceservice
|
||||||
from sugar3.activity.activityservice import ActivityService
|
from sugar3.activity.activityservice import ActivityService
|
||||||
from sugar3.graphics import style
|
from sugar3.graphics import style
|
||||||
@ -90,6 +90,9 @@ from sugar3.graphics.icon import Icon
|
|||||||
from sugar3.datastore import datastore
|
from sugar3.datastore import datastore
|
||||||
from sugar3.bundle.activitybundle import get_bundle_instance
|
from sugar3.bundle.activitybundle import get_bundle_instance
|
||||||
from sugar3.bundle.helpers import bundle_from_dir
|
from sugar3.bundle.helpers import bundle_from_dir
|
||||||
|
from sugar3 import env
|
||||||
|
from errno import EEXIST
|
||||||
|
|
||||||
from gi.repository import SugarExt
|
from gi.repository import SugarExt
|
||||||
|
|
||||||
_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
|
_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
|
||||||
@ -436,13 +439,18 @@ class Activity(Window, Gtk.Container):
|
|||||||
self.__jobject_updated_cb)
|
self.__jobject_updated_cb)
|
||||||
self.set_title(self._jobject.metadata['title'])
|
self.set_title(self._jobject.metadata['title'])
|
||||||
|
|
||||||
|
if 'SUGAR_VERSION' not in os.environ:
|
||||||
|
bundle = get_bundle_instance(get_bundle_path())
|
||||||
|
self.set_icon_from_file(bundle.get_icon())
|
||||||
|
|
||||||
|
|
||||||
def run_main_loop(self):
|
def run_main_loop(self):
|
||||||
Gtk.main()
|
Gtk.main()
|
||||||
|
|
||||||
def _initialize_journal_object(self):
|
def _initialize_journal_object(self):
|
||||||
title = _('%s Activity') % get_bundle_name()
|
title = _('%s Activity') % get_bundle_name()
|
||||||
settings = Gio.Settings('org.sugarlabs.user')
|
|
||||||
icon_color = settings.get_string('color')
|
icon_color = get_color().to_string()
|
||||||
|
|
||||||
jobject = datastore.create()
|
jobject = datastore.create()
|
||||||
jobject.metadata['title'] = title
|
jobject.metadata['title'] = title
|
||||||
@ -613,11 +621,12 @@ class Activity(Window, Gtk.Container):
|
|||||||
|
|
||||||
def _adapt_window_to_screen(self):
|
def _adapt_window_to_screen(self):
|
||||||
screen = Gdk.Screen.get_default()
|
screen = Gdk.Screen.get_default()
|
||||||
|
workarea = screen.get_monitor_workarea(screen.get_number())
|
||||||
geometry = Gdk.Geometry()
|
geometry = Gdk.Geometry()
|
||||||
geometry.max_width = geometry.base_width = geometry.min_width = \
|
geometry.max_width = geometry.base_width = geometry.min_width = \
|
||||||
screen.get_width()
|
workarea.width
|
||||||
geometry.max_height = geometry.base_height = geometry.min_height = \
|
geometry.max_height = geometry.base_height = geometry.min_height = \
|
||||||
screen.get_height()
|
workarea.height
|
||||||
geometry.width_inc = geometry.height_inc = geometry.min_aspect = \
|
geometry.width_inc = geometry.height_inc = geometry.min_aspect = \
|
||||||
geometry.max_aspect = 1
|
geometry.max_aspect = 1
|
||||||
hints = Gdk.WindowHints(Gdk.WindowHints.ASPECT |
|
hints = Gdk.WindowHints(Gdk.WindowHints.ASPECT |
|
||||||
@ -671,7 +680,7 @@ class Activity(Window, Gtk.Container):
|
|||||||
if os.environ.get('SUGAR_ACTIVITY_ROOT'):
|
if os.environ.get('SUGAR_ACTIVITY_ROOT'):
|
||||||
return os.environ['SUGAR_ACTIVITY_ROOT']
|
return os.environ['SUGAR_ACTIVITY_ROOT']
|
||||||
else:
|
else:
|
||||||
return '/'
|
return get_activity_root()
|
||||||
|
|
||||||
def read_file(self, file_path):
|
def read_file(self, file_path):
|
||||||
'''
|
'''
|
||||||
@ -876,7 +885,7 @@ class Activity(Window, Gtk.Container):
|
|||||||
if not self.metadata.get('activity_id', ''):
|
if not self.metadata.get('activity_id', ''):
|
||||||
self.metadata['activity_id'] = self.get_id()
|
self.metadata['activity_id'] = self.get_id()
|
||||||
|
|
||||||
file_path = os.path.join(self.get_activity_root(), 'instance',
|
file_path = os.path.join(get_activity_root(), 'instance',
|
||||||
'%i' % time.time())
|
'%i' % time.time())
|
||||||
try:
|
try:
|
||||||
self.write_file(file_path)
|
self.write_file(file_path)
|
||||||
@ -1251,7 +1260,13 @@ def get_activity_root():
|
|||||||
if os.environ.get('SUGAR_ACTIVITY_ROOT'):
|
if os.environ.get('SUGAR_ACTIVITY_ROOT'):
|
||||||
return os.environ['SUGAR_ACTIVITY_ROOT']
|
return os.environ['SUGAR_ACTIVITY_ROOT']
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('No SUGAR_ACTIVITY_ROOT set.')
|
activity_root = env.get_profile_path(os.environ['SUGAR_BUNDLE_ID'])
|
||||||
|
try:
|
||||||
|
os.mkdir(activity_root)
|
||||||
|
except OSError, e:
|
||||||
|
if e.errno != EEXIST:
|
||||||
|
raise e
|
||||||
|
return activity_root
|
||||||
|
|
||||||
|
|
||||||
def show_object_in_journal(object_id):
|
def show_object_in_journal(object_id):
|
||||||
|
@ -32,6 +32,7 @@ import dbus
|
|||||||
from sugar3 import env
|
from sugar3 import env
|
||||||
from sugar3 import mime
|
from sugar3 import mime
|
||||||
from sugar3 import dispatch
|
from sugar3 import dispatch
|
||||||
|
from sugar3.profile import get_color
|
||||||
|
|
||||||
DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore'
|
DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore'
|
||||||
DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore'
|
DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore'
|
||||||
@ -222,7 +223,6 @@ class RawObject(object):
|
|||||||
|
|
||||||
def __init__(self, file_path):
|
def __init__(self, file_path):
|
||||||
stat = os.stat(file_path)
|
stat = os.stat(file_path)
|
||||||
settings = Gio.Settings('org.sugarlabs.user')
|
|
||||||
metadata = {
|
metadata = {
|
||||||
'uid': file_path,
|
'uid': file_path,
|
||||||
'title': os.path.basename(file_path),
|
'title': os.path.basename(file_path),
|
||||||
@ -230,7 +230,7 @@ class RawObject(object):
|
|||||||
'mime_type': Gio.content_type_guess(file_path, None)[0],
|
'mime_type': Gio.content_type_guess(file_path, None)[0],
|
||||||
'activity': '',
|
'activity': '',
|
||||||
'activity_id': '',
|
'activity_id': '',
|
||||||
'icon-color': settings.get_string('color'),
|
'icon-color': get_color().to_string(),
|
||||||
'description': file_path,
|
'description': file_path,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,9 +116,13 @@ MEDIUM_ICON_SIZE = zoom(55 * 1.5)
|
|||||||
LARGE_ICON_SIZE = zoom(55 * 2.0)
|
LARGE_ICON_SIZE = zoom(55 * 2.0)
|
||||||
XLARGE_ICON_SIZE = zoom(55 * 2.75)
|
XLARGE_ICON_SIZE = zoom(55 * 2.75)
|
||||||
|
|
||||||
settings = Gio.Settings('org.sugarlabs.font')
|
if 'org.sugarlabs.font' in Gio.Settings.list_schemas():
|
||||||
FONT_SIZE = settings.get_double('default-size')
|
settings = Gio.Settings('org.sugarlabs.font')
|
||||||
FONT_FACE = settings.get_string('default-face')
|
FONT_SIZE = settings.get_double('default-size')
|
||||||
|
FONT_FACE = settings.get_string('default-face')
|
||||||
|
else:
|
||||||
|
FONT_SIZE = 10
|
||||||
|
FONT_FACE = 'Sans Serif'
|
||||||
|
|
||||||
FONT_NORMAL = Font('%s %f' % (FONT_FACE, FONT_SIZE))
|
FONT_NORMAL = Font('%s %f' % (FONT_FACE, FONT_SIZE))
|
||||||
FONT_BOLD = Font('%s bold %f' % (FONT_FACE, FONT_SIZE))
|
FONT_BOLD = Font('%s bold %f' % (FONT_FACE, FONT_SIZE))
|
||||||
|
@ -244,6 +244,7 @@ class XoColor:
|
|||||||
parsed_color = None
|
parsed_color = None
|
||||||
|
|
||||||
if color_string is None:
|
if color_string is None:
|
||||||
|
if 'org.sugarlabs.user' in Gio.Settings.list_schemas():
|
||||||
settings = Gio.Settings('org.sugarlabs.user')
|
settings = Gio.Settings('org.sugarlabs.user')
|
||||||
color_string = settings.get_string('color')
|
color_string = settings.get_string('color')
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ STABLE.
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from gi.repository import GObject
|
from gi.repository import GObject
|
||||||
from gi.repository import Gio
|
|
||||||
import dbus
|
import dbus
|
||||||
from telepathy.interfaces import CONNECTION, \
|
from telepathy.interfaces import CONNECTION, \
|
||||||
CONNECTION_INTERFACE_ALIASING, \
|
CONNECTION_INTERFACE_ALIASING, \
|
||||||
@ -32,6 +31,7 @@ from telepathy.interfaces import CONNECTION, \
|
|||||||
from telepathy.constants import HANDLE_TYPE_CONTACT
|
from telepathy.constants import HANDLE_TYPE_CONTACT
|
||||||
|
|
||||||
from sugar3.presence.connectionmanager import get_connection_manager
|
from sugar3.presence.connectionmanager import get_connection_manager
|
||||||
|
from sugar3.profile import get_color, get_nick_name
|
||||||
|
|
||||||
ACCOUNT_MANAGER_SERVICE = 'org.freedesktop.Telepathy.AccountManager'
|
ACCOUNT_MANAGER_SERVICE = 'org.freedesktop.Telepathy.AccountManager'
|
||||||
CONN_INTERFACE_BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo'
|
CONN_INTERFACE_BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo'
|
||||||
@ -243,6 +243,6 @@ class Owner(BaseBuddy):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
BaseBuddy.__init__(self)
|
BaseBuddy.__init__(self)
|
||||||
|
|
||||||
settings = Gio.Settings('org.sugarlabs.user')
|
self.props.nick = get_nick_name()
|
||||||
self.props.nick = settings.get_string('nick')
|
self.props.color = get_color().to_string()
|
||||||
self.props.color = settings.get_string('color')
|
|
||||||
|
@ -27,6 +27,7 @@ from sugar3 import env
|
|||||||
from sugar3 import util
|
from sugar3 import util
|
||||||
from sugar3.graphics.xocolor import XoColor
|
from sugar3.graphics.xocolor import XoColor
|
||||||
|
|
||||||
|
import getpass
|
||||||
|
|
||||||
_profile = None
|
_profile = None
|
||||||
|
|
||||||
@ -62,9 +63,8 @@ class Profile(object):
|
|||||||
privkey_hash = property(fget=_get_privkey_hash)
|
privkey_hash = property(fget=_get_privkey_hash)
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
settings = Gio.Settings('org.sugarlabs.user')
|
nick = get_nick_name()
|
||||||
nick = settings.get_string('nick')
|
color = get_color()
|
||||||
color = settings.get_string('color')
|
|
||||||
|
|
||||||
return nick is not '' and \
|
return nick is not '' and \
|
||||||
color is not '' and \
|
color is not '' and \
|
||||||
@ -204,14 +204,20 @@ def get_profile():
|
|||||||
|
|
||||||
|
|
||||||
def get_nick_name():
|
def get_nick_name():
|
||||||
|
if 'org.sugarlabs.user' in Gio.Settings.list_schemas():
|
||||||
settings = Gio.Settings('org.sugarlabs.user')
|
settings = Gio.Settings('org.sugarlabs.user')
|
||||||
return settings.get_string('nick')
|
return settings.get_string('nick')
|
||||||
|
else:
|
||||||
|
return getpass.getuser()
|
||||||
|
|
||||||
|
|
||||||
def get_color():
|
def get_color():
|
||||||
|
if 'org.sugarlabs.user' in Gio.Settings.list_schemas():
|
||||||
settings = Gio.Settings('org.sugarlabs.user')
|
settings = Gio.Settings('org.sugarlabs.user')
|
||||||
color = settings.get_string('color')
|
color = settings.get_string('color')
|
||||||
return XoColor(color)
|
return XoColor(color)
|
||||||
|
else:
|
||||||
|
return XoColor()
|
||||||
|
|
||||||
|
|
||||||
def get_pubkey():
|
def get_pubkey():
|
||||||
|
Loading…
Reference in New Issue
Block a user