From e96c9ecb3491127a585da643f14930659275a374 Mon Sep 17 00:00:00 2001 From: Walter Bender Date: Fri, 30 May 2014 16:31:21 -0400 Subject: [PATCH 1/4] Add configurable database for age/gender selection OLPC AU has asked to use grade-level instead of age in its intro and aboutme sections. This patch enables this change by introducing a new configuration file, group-labels.defaults into sugar/data. The file is used by the age picker to determine which icons to use and what labels to use for the icons. Internally, it still stores age as a birthdate timestamp in gsettings. Additonal methods are included for converting between age and the configuration. In this version of the patch, the configuration file is for grade level. An age configuration file will be proposed for the upstream patch, which will leave the user experience unchanged from the current default for Sugar users. A few other changes included in this patch: (1) a reset button is provided for clearing the gender selection (required by law in some places, including AU); (2) consolidation of the age and gender code used by both the intro screens and aboutme cpsection; (3) provision for reformating the age panel if screen rotation makes the panel too wide to fit. (3) to meet another requirement for AU, the group label setting is saved to gesttings in order to determine whether or not the group label has ever been set. It has not been set, the age intro page is shown boot. --- bin/sugar.in | 4 + data/Makefile.am | 1 + data/group-labels.defaults | 1 + data/org.sugarlabs.gschema.xml | 5 + extensions/cpsection/aboutme/model.py | 71 ------ extensions/cpsection/aboutme/view.py | 166 ++++---------- src/jarabe/intro/__init__.py | 13 ++ src/jarabe/intro/agepicker.py | 299 +++++++++++++++++++++++--- src/jarabe/intro/genderpicker.py | 45 +++- src/jarabe/intro/window.py | 59 ++--- src/jarabe/main.py | 12 +- 11 files changed, 401 insertions(+), 275 deletions(-) create mode 100644 data/group-labels.defaults diff --git a/bin/sugar.in b/bin/sugar.in index aebb8978ab..366ede2eec 100644 --- a/bin/sugar.in +++ b/bin/sugar.in @@ -20,6 +20,10 @@ if test -z "$SUGAR_SCALING"; then export SUGAR_SCALING=72 fi +if test -z "$SUGAR_GROUP_LABELS"; then + export SUGAR_GROUP_LABELS="$sugardatadir/group-labels.defaults" +fi + if test -z "$SUGAR_MIME_DEFAULTS"; then export SUGAR_MIME_DEFAULTS="$sugardatadir/mime.defaults" fi diff --git a/data/Makefile.am b/data/Makefile.am index 01ceb525c8..2f7b6d5a99 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -11,6 +11,7 @@ sugar-100.gtkrc: gtkrc.em sugardir = $(pkgdatadir)/data sugar_DATA = \ activities.defaults \ + group-labels.defaults \ ISO-639-2_utf-8.txt \ kbdconfig \ mime.defaults \ diff --git a/data/group-labels.defaults b/data/group-labels.defaults new file mode 100644 index 0000000000..9bf3569729 --- /dev/null +++ b/data/group-labels.defaults @@ -0,0 +1 @@ +{"group-label": "Select grade:", "group-items": [{"female-icon": "female-1", "male-icon": "male-1", "label": "Preschool", "age": 4}, {"female-icon": "female-1", "male-icon": "male-1", "label": "Kindergarten", "age": 5}, {"female-icon": "female-2", "male-icon": "male-2", "label": "1st Grade", "age": 6}, {"female-icon": "female-3", "male-icon": "male-3", "label": "2nd Grade", "age": 7}, {"female-icon": "female-4", "male-icon": "male-4", "label": "3rd Grade", "age": 8}, {"female-icon": "female-5", "male-icon": "male-5", "label": "4th Grade", "age": 9}, {"female-icon": "female-5", "male-icon": "male-5", "label": "5th Grade", "age": 10}, {"female-icon": "female-6", "male-icon": "male-6", "label": "6th Grade", "age": 11}, {"female-icon": "female-6", "male-icon": "male-6", "label": "7th Grade", "age": 12}, {"female-icon": "female-7", "male-icon": "male-7", "label": "High School", "age": 13}, {"female-icon": "female-7", "male-icon": "male-7", "label": "Adult", "age": 25}]} diff --git a/data/org.sugarlabs.gschema.xml b/data/org.sugarlabs.gschema.xml index 3ae041cb49..1971c1b22f 100644 --- a/data/org.sugarlabs.gschema.xml +++ b/data/org.sugarlabs.gschema.xml @@ -82,6 +82,11 @@ User Birth Timestamp Birth timestamp (seconds since the epoch) + + '' + Group Label + Label associated with age, e.g., '2nd Grade' + diff --git a/extensions/cpsection/aboutme/model.py b/extensions/cpsection/aboutme/model.py index a3d89bca76..e60e9c5fa1 100644 --- a/extensions/cpsection/aboutme/model.py +++ b/extensions/cpsection/aboutme/model.py @@ -24,9 +24,6 @@ from sugar3 import profile -from jarabe.intro.window import calculate_birth_timestamp, calculate_age -from jarabe.intro.agepicker import AGES - _COLORS = { 'red': {'dark': '#b20008', 'medium': '#e6000a', 'light': '#ffadce'}, 'orange': {'dark': '#9a5200', 'medium': '#c97e00', 'light': '#ffc169'}, @@ -145,71 +142,3 @@ def set_color_xo(color): client = GConf.Client.get_default() client.set_string('/desktop/sugar/user/color', color) return 1 - - -def get_gender(): - settings = Gio.Settings('org.sugarlabs.user') - return settings.get_string('gender') - - -def print_gender(): - print get_gender() - - -def set_gender(gender): - """Set the gender, e.g. 'female' - """ - if not gender: - gender = '' # default value in gsettings indicates no gender selected - elif gender not in ['male', 'female', '']: - raise ValueError(_('Gender must be male or female.')) - - settings = Gio.Settings('org.sugarlabs.user') - settings.set_string('gender', gender) - return - - -def get_age(): - settings = Gio.Settings('org.sugarlabs.user') - birth_timestamp = settings.get_int('birth-timestamp') - - if birth_timestamp == 0: - return None - - birth_age = calculate_age(birth_timestamp) - - age = (AGES[-2] + AGES[-1]) / 2. - if birth_age >= age: - return AGES[-1] - - for i in range(len(AGES) - 1): - age = (AGES[i] + AGES[i + 1]) / 2. - if birth_age < age: - return AGES[i] - - return None - - -def print_age(): - print get_age() - - -def set_age(age): - """Set the age and an approximate birth timestamp - age: e.g. 8 - birth_timestamp: time - age * #seconds per year - """ - try: - i = int(age) - except ValueError, e: - logging.error('set_age: %s' % (e)) - i = None - - if i is None or i < 1: - raise ValueError(_('Age must be a positive integer.')) - - birth_timestamp = calculate_birth_timestamp(age) - - settings = Gio.Settings('org.sugarlabs.user') - settings.set_int('birth-timestamp', birth_timestamp) - return diff --git a/extensions/cpsection/aboutme/view.py b/extensions/cpsection/aboutme/view.py index 287bd02979..4c8d01c0f2 100644 --- a/extensions/cpsection/aboutme/view.py +++ b/extensions/cpsection/aboutme/view.py @@ -18,8 +18,10 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from gi.repository import Gtk +from gi.repository import Gdk from gi.repository import GObject from gettext import gettext as _ +import logging from sugar3.graphics import style from sugar3.graphics.xocolor import XoColor, colors @@ -27,7 +29,8 @@ from jarabe.controlpanel.sectionview import SectionView from jarabe.controlpanel.inlinealert import InlineAlert -from jarabe.intro.agepicker import AGES, AGE_LABELS +from jarabe.intro.agepicker import AgePicker, save_age, load_age +from jarabe.intro.genderpicker import GenderPicker, save_gender, load_gender _STROKE_COLOR = 0 @@ -149,94 +152,11 @@ def __pressed_cb(self, button, event, picker): self.color_changed_signal.emit(self._color) -class GenderPicker(EventIcon): - gender_changed_signal = GObject.Signal('gender-changed', - arg_types=([str])) - - def __init__(self, color, gender): - EventIcon.__init__(self, icon_name='%s-6' % (gender), - pixel_size=style.XLARGE_ICON_SIZE) - self._gender = gender - self._color = color - - self.set_gender() - - self.connect('button_press_event', self.__pressed_cb) - - def set_color(self, color, gender): - self._color = color - self.set_gender(gender) - - def set_gender(self, gender=''): - if gender is not '' and self._gender == gender: - self.props.xo_color = self._color - else: - self.props.xo_color = _NOCOLOR - - gender = GObject.property(type=object, setter=set_gender) - - def __pressed_cb(self, button, event): - self.gender_changed_signal.emit(self._gender) - - -class AgePicker(Gtk.Grid): - - age_changed_signal = GObject.Signal('age-changed', - arg_types=([int])) - - def __init__(self, color, gender, age): - Gtk.Grid.__init__(self) - self._color = color - self._gender = gender - self._age = age - - if self._gender is '': - # Used for graphic only; does not set user's gender preference. - self._gender = 'female' - - self._icon = EventIcon(icon_name='%s-%d' % (self._gender, self._age), - pixel_size=style.LARGE_ICON_SIZE) - self._icon.connect('button-press-event', self.__pressed_cb) - self.attach(self._icon, 0, 0, 1, 1) - self._icon.show() - - label = Gtk.Label() - label.set_text(AGE_LABELS[self._age]) - self.attach(label, 0, 1, 1, 1) - label.show() - - self.set_age() - - def set_color(self, color, age): - self._color = color - self.set_age(age) - - def set_age(self, age=None): - if age in AGES: - age_index = AGES.index(age) - else: - age_index = None - - if age_index == self._age: - self._icon.props.xo_color = self._color - else: - self._icon.props.xo_color = _NOCOLOR - self._icon.show() - - age = GObject.property(type=object, setter=set_age) - - def set_gender(self, gender): - self._icon.set_icon_name('%s-%d' % (gender, self._age)) - self._icon.show() - - gender = GObject.property(type=object, setter=set_gender) - - def __pressed_cb(self, button, event): - self.age_changed_signal.emit(self._age) - - class AboutMe(SectionView): + age_changed_signal = GObject.Signal('age-changed', arg_types=([int])) + gender_changed_signal = GObject.Signal('gender-changed', arg_types=([str])) + def __init__(self, model, alerts): SectionView.__init__(self) @@ -272,11 +192,10 @@ def __init__(self, model, alerts): for picker in self._pickers.values(): picker.connect('color-changed', self.__color_changed_cb) - self._female_picker.connect('gender-changed', self.__gender_changed_cb) - self._male_picker.connect('gender-changed', self.__gender_changed_cb) + self._gender_pickers.connect('gender-changed', + self.__gender_changed_cb) + self._age_pickers.connect('age-changed', self.__age_changed_cb) - for picker in self._age_pickers: - picker.connect('age-changed', self.__age_changed_cb) def _setup_nick(self): grid = Gtk.Grid() @@ -366,7 +285,9 @@ def _setup_color(self): center_in_panel.show() def _setup_gender(self): - self._gender = self._model.get_gender() + self._saved_gender = load_gender() + + self._gender_pickers = GenderPicker() grid = Gtk.Grid() grid.set_row_spacing(style.DEFAULT_SPACING) @@ -378,15 +299,8 @@ def _setup_gender(self): grid.attach(label_gender, 0, 0, 1, 1) label_gender.show() - self._female_picker = GenderPicker(self._color, 'female') - grid.attach(self._female_picker, 0, 1, 1, 1) - self._female_picker.props.gender = self._gender - self._female_picker.show() - - self._male_picker = GenderPicker(self._color, 'male') - grid.attach(self._male_picker, 1, 1, 1, 1) - self._male_picker.props.gender = self._gender - self._male_picker.show() + grid.attach(self._gender_pickers, 0, 1, 1, 1) + self._gender_pickers.show() center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) center_in_panel.add(grid) @@ -396,26 +310,30 @@ def _setup_gender(self): center_in_panel.show() def _setup_age(self): - self._age = self._model.get_age() + self._saved_age = load_age() grid = Gtk.Grid() grid.set_row_spacing(style.DEFAULT_SPACING) grid.set_column_spacing(style.DEFAULT_SPACING) - self._age_pickers = [] - for i in range(len(AGES)): - self._age_pickers.append(AgePicker(self._color, self._gender, i)) + self._age_pickers = AgePicker(self._gender) + center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) + center_in_panel.add(self._age_pickers) + self._age_pickers.show() + + label = self._age_pickers.get_label() - label_age = Gtk.Label(label=_('Select age:')) + label_age = Gtk.Label(label=_(label)) label_age.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) - grid.attach(label_age, 0, 0, 1, 1) + left_align = Gtk.Alignment.new(0, 0, 0, 0) + left_align.add(label_age) label_age.show() + grid.attach(left_align, 0, 0, 1, 1) + left_align.show() - for i in range(len(AGES)): - grid.attach(self._age_pickers[i], i, 1, 1, 1) - self._age_pickers[i].set_age(self._age) - self._age_pickers[i].show() + grid.attach(center_in_panel, 0, 1, 1, 1) + center_in_panel.show() center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) center_in_panel.add(grid) @@ -431,16 +349,15 @@ def undo(self): self._nick_alert.hide() self._color_alert.hide() - self._model.set_gender(self._gender) - self._model.set_age(self._age) + # Undo gender or age changes + save_gender(self._saved_gender) + save_age(self._saved_age) def _update_pickers(self, color): for picker in self._pickers.values(): picker.props.color = color - self._female_picker.set_color(color, self._gender) - self._male_picker.set_color(color, self._gender) - for i in range(len(AGES)): - self._age_pickers[i].set_color(color, self._age) + self._gender_pickers.update_color(color) + self._age_pickers.update_color(color) def _validate(self): if self._nick_valid and self._color_valid: @@ -498,16 +415,9 @@ def __color_changed_cb(self, colorpicker, color): return False def __gender_changed_cb(self, genderpicker, gender): - self._model.set_gender(gender) - self._female_picker.props.gender = gender - self._female_picker.props.gender = gender - self._male_picker.props.gender = gender - for i in range(len(AGES)): - self._age_pickers[i].props.gender = gender + save_gender(gender) + self._age_pickers.update_gender(gender) return False - def __age_changed_cb(self, agepicker, age): - self._model.set_age(AGES[age]) - for i in range(len(AGES)): - self._age_pickers[i].props.age = AGES[age] - return False + def __age_changed_cb(self, event, age): + save_age(age) diff --git a/src/jarabe/intro/__init__.py b/src/jarabe/intro/__init__.py index 5c243025d1..2766f01198 100644 --- a/src/jarabe/intro/__init__.py +++ b/src/jarabe/intro/__init__.py @@ -1,5 +1,7 @@ import os +from gi.repository import Gio + from sugar3 import env from sugar3.profile import get_profile @@ -12,3 +14,14 @@ def check_profile(): profile.convert_profile() return profile.is_valid() + + +def check_group_label(): + settings = Gio.Settings('org.sugarlabs.user') + if len(settings.get_string('group-label')) > 0: + return True + + # DEPRECATED + from gi.repository import GConf + client = GConf.Client.get_default() + return client.get_string('/desktop/sugar/user/group') is not None diff --git a/src/jarabe/intro/agepicker.py b/src/jarabe/intro/agepicker.py index 1e861654d5..87d1a73b63 100644 --- a/src/jarabe/intro/agepicker.py +++ b/src/jarabe/intro/agepicker.py @@ -17,6 +17,14 @@ from gi.repository import Gtk from gi.repository import Gdk +from gi.repository import Gio +from gi.repository import GObject + +import os +import json +import time +import math +import logging from gettext import gettext as _ @@ -24,65 +32,290 @@ from sugar3.graphics import style from sugar3.graphics.xocolor import XoColor -AGES = [3, 5, 7, 9, 11, 12, 15, 25] -AGE_LABELS = [_('0-3'), _('4-5'), _('6-7'), _('8-9'), _('10-11'), _('12'), - _('13-17'), _('Adult')] +from jarabe.intro.genderpicker import GENDERS + +_group_labels = None +_SECONDS_PER_YEAR = 365 * 24 * 60 * 60. + + +def calculate_birth_timestamp(age): + age_in_seconds = age * _SECONDS_PER_YEAR + birth_timestamp = int(time.time() - age_in_seconds) + return birth_timestamp + + +def calculate_age(birth_timestamp): + age_in_seconds = time.time() - birth_timestamp + # Round to nearest int + age = int(math.floor(age_in_seconds / _SECONDS_PER_YEAR) + 0.5) + return age + + +def age_to_index(age): + group_labels = get_group_labels() + + if age is None: + return int(len(group_labels.AGES) / 2.0) + + age2 = age * 2 + for i in range(len(group_labels.AGES) - 1): + if age2 < (group_labels.AGES[i] + group_labels.AGES[i + 1]): + return i + + return len(group_labels.AGES) - 1 + + +def age_to_group_label(age): + group_labels = get_group_labels() + + return group_labels.LABELS[age_to_index(age)] + + +def group_label_to_age(label): + group_labels = get_group_labels() + + if labels not in group_labels.LABELS: + return None + + return group_labels.AGES[group_labels.LABELS.index(label)] + + +def load_age(): + group_labels = get_group_labels() + + settings = Gio.Settings('org.sugarlabs.user') + birth_timestamp = settings.get_int('birth-timestamp') + + if birth_timestamp == 0: + return None + + birth_age = calculate_age(birth_timestamp) + + age = (group_labels.AGES[-2] + group_labels.AGES[-1]) / 2. + if birth_age >= age: + return group_labels.AGES[-1] + + for i in range(len(group_labels.AGES) - 1): + age = (group_labels.AGES[i] + group_labels.AGES[i + 1]) / 2. + if birth_age < age: + return group_labels.AGES[i] + + return None + + +def save_age(age): + birth_timestamp = calculate_birth_timestamp(age) + settings = Gio.Settings('org.sugarlabs.user') + settings.set_int('birth-timestamp', birth_timestamp) + + # Record the label so we know it was set + settings.set_string('group-label', age_to_group_label(age)) + + # DEPRECATED + from gi.repository import GConf + client = GConf.Client.get_default() + client.set_int('/desktop/sugar/user/birth_timestamp', birth_timestamp) + + +class GroupLabels(): + GROUP_LABEL = [] + AGES = [] + LABELS = [] + ICONS = [] + + def __init__(self): + f = open(os.environ['SUGAR_GROUP_LABELS'], 'r') + json_data = f.read() + f.close() + group_labels = json.loads(json_data) + self.GROUP_LABEL = group_labels['group-label'] + for item in group_labels['group-items']: + self.ICONS.append([item['female-icon'], item['male-icon']]) + self.LABELS.append(_(item['label'])) + self.AGES.append(item['age']) + + +def get_group_labels(): + global _group_labels + + if not _group_labels: + _group_labels = GroupLabels() + + return _group_labels + + +class Picker(Gtk.Grid): + + def __init__(self, icon, label): + Gtk.Grid.__init__(self) + + self._button = EventIcon(pixel_size=style.LARGE_ICON_SIZE, + icon_name=icon) + self.attach(self._button, 0, 0, 1, 1) + self._button.hide() + + self._label = Gtk.Label(label) + self.attach(self._label, 0, 1, 1, 1) + self._label.hide() + + def show_all(self): + self._button.show() + self._label.show() + self.show() + + def hide_all(self): + self._button.hide() + self._label.hide() + self.hide() + + def connect(self, callback, arg): + self._button.connect('button-press-event', callback, arg) + + def set_color(self, color): + self._button.xo_color = color + + def set_icon(self, icon): + self._button.set_icon_name(icon) class AgePicker(Gtk.Grid): - def __init__(self, gender): + age_changed_signal = GObject.Signal('age-changed', arg_types=([int])) + + def __init__(self, gender, page=None): Gtk.Grid.__init__(self) + self.set_row_spacing(style.DEFAULT_SPACING) self.set_column_spacing(style.DEFAULT_SPACING) + self._group_labels = get_group_labels() + + self._page = page self._gender = gender - self._age = 5 - self._buttons = [] + self._age = self.get_age() + self._pickers = [] self._nocolor = XoColor('#010101,#ffffff') self._color = XoColor() - if self._gender is None or self._gender == 'None': - self._gender = 'male' + if self._gender not in GENDERS: + self._gender = 'female' + + gender_index = GENDERS.index(self._gender) + age_index = age_to_index(self._age) + + width = Gdk.Screen.width() + + num_ages = len(self._group_labels.AGES) + for i in range(num_ages): + self._pickers.append( + Picker(self._group_labels.ICONS[i][gender_index], + _(self._group_labels.LABELS[i]))) + self._pickers[i].connect(self._button_press_cb, i) + + self._fixed = Gtk.Fixed() + fixed_size = width - 4 * style.GRID_CELL_SIZE + self._fixed.set_size_request(fixed_size, -1) + self.attach(self._fixed, 0, 0, 1, 1) + self._fixed.show() - for i in range(len(AGES)): - self._buttons.append( - EventIcon(pixel_size=style.LARGE_ICON_SIZE, - icon_name='%s-%d' % (self._gender, i))) - self._buttons[-1].show() - self._buttons[-1].connect('button-press-event', - self._button_press_cb, i) + self._age_adj = Gtk.Adjustment(value=age_index, lower=0, + upper=num_ages - 1, step_incr=1, + page_incr=3, page_size=0) + self._age_adj.connect('value-changed', self.__age_adj_changed_cb) - label = Gtk.Label() - label.set_text(AGE_LABELS[i]) - label.show() + self._age_slider = Gtk.HScale() + self._age_slider.set_draw_value(False) + self._age_slider.set_adjustment(self._age_adj) + self.attach(self._age_slider, 0, 1, 1, 1) - self.attach(self._buttons[-1], i, 0, 1, 1) - self.attach(label, i, 1, 1, 1) + for i in range(num_ages): + self._fixed.put(self._pickers[i], 0, 0) - def _button_press_cb(self, widget, event, age): + self._configure(width) + + Gdk.Screen.get_default().connect('size-changed', self._configure_cb) + + def _configure_cb(self, event=None): + width = Gdk.Screen.width() + self._configure(width) + + def _configure(self, width): + fixed_size = width - 4 * style.GRID_CELL_SIZE + self._fixed.set_size_request(fixed_size, -1) + + num_ages = len(self._group_labels.AGES) + + dx = int((fixed_size - style.LARGE_ICON_SIZE) / (num_ages - 1)) + for i in range(num_ages): + self._fixed.move(self._pickers[i], dx * i, 0) + + if num_ages + 2 < width / style.LARGE_ICON_SIZE: + for i in range(num_ages): + self._pickers[i].show_all() + self._age_slider.hide() + else: + self._age_slider.show() + value = self._age_adj.get_value() + self._set_age_picker(int(value + 0.5)) + + def get_label(self): + return self._group_labels.GROUP_LABEL + + def _set_age_picker(self, age_index): + for i in range(len(self._group_labels.AGES)): + if i == age_index: + self._pickers[i].show_all() + else: + self._pickers[i].hide_all() + self._do_selected(age_index) + + def __age_adj_changed_cb(self, widget): + value = self._age_adj.get_value() + self._set_age_picker(int(value + 0.5)) + + def _do_selected(self, age_index): + if self._age is not None: + i = age_to_index(self._age) + self._pickers[i].set_color(self._nocolor) + self._set_age(self._group_labels.AGES[age_index]) + self._pickers[age_index].set_color(self._color) + + def _button_press_cb(self, widget, event, age_index): if event.button == 1 and event.type == Gdk.EventType.BUTTON_PRESS: - if self._age is not None: - self._buttons[self._age].xo_color = self._nocolor - self._set_age(age) - self._buttons[age].xo_color = self._color + self._do_selected(age_index) def get_age(self): - if self._age is None: - return None - else: - return AGES[self._age] + if self._page is None: + return load_age() + elif hasattr(self, '_age'): + if self._age is None: + return None + i = age_to_index(self._age) + return self._group_labels.AGES[i] + return None def _set_age(self, age): + if self._page is None: + if age != self._age: + self.age_changed_signal.emit(age) + else: + self._page.set_valid(True) self._age = age def update_color(self, color): self._color = color if self._age is not None: - self._buttons[self._age].xo_color = self._color + i = age_to_index(self._age) + self._pickers[i].set_color(self._color) def update_gender(self, gender): self._gender = gender - for i in range(8): - self._buttons[i].set_icon_name('%s-%d' % (self._gender, i)) - self._buttons[i].show() + + if self._gender in GENDERS: + gender_index = GENDERS.index(self._gender) + else: + gender_index = 0 + + for i in range(len(self._group_labels.AGES)): + self._pickers[i].set_icon( + self._group_labels.ICONS[i][gender_index]) diff --git a/src/jarabe/intro/genderpicker.py b/src/jarabe/intro/genderpicker.py index 7df0e82674..dfd895caf3 100644 --- a/src/jarabe/intro/genderpicker.py +++ b/src/jarabe/intro/genderpicker.py @@ -17,6 +17,8 @@ from gi.repository import Gtk from gi.repository import Gdk +from gi.repository import Gio +from gi.repository import GObject from sugar3.graphics.icon import EventIcon from sugar3.graphics import style @@ -26,13 +28,35 @@ GENDERS = ['female', 'male'] +def load_gender(): + settings = Gio.Settings('org.sugarlabs.user') + return settings.get_string('gender') + + +def save_gender(gender): + settings = Gio.Settings('org.sugarlabs.user') + if gender is not None: + settings.set_string('gender', gender) + else: + settings.set_string('gender', '') + + # DEPRECATED + from gi.repository import GConf + if gender is not None: + client = GConf.Client.get_default() + client.set_string('/desktop/sugar/user/gender', gender) + + class GenderPicker(Gtk.Grid): + + gender_changed_signal = GObject.Signal('gender-changed', arg_types=([str])) + def __init__(self): Gtk.Grid.__init__(self) self.set_row_spacing(style.DEFAULT_SPACING) self.set_column_spacing(style.DEFAULT_SPACING) - self._gender = None + self._gender = self.get_gender() self._buttons = [] self._nocolor = XoColor('#010101,#ffffff') self._color = XoColor() @@ -40,11 +64,23 @@ def __init__(self): for i, gender in enumerate(GENDERS): self._buttons.append(EventIcon(pixel_size=style.XLARGE_ICON_SIZE, icon_name='%s-6' % (gender))) - self._buttons[-1].show() self._buttons[-1].connect('button-press-event', self._button_press_cb, i) + self.attach(self._buttons[-1], i * 2, 0, 1, 1) + self._buttons[-1].show() + + self.reset_button = EventIcon(pixel_size=style.SMALL_ICON_SIZE, + icon_name='entry-cancel') + self.reset_button.connect('button-press-event', + self._reset_button_press_cb) + self.attach(self.reset_button, 1, 0, 1, 1) + self.reset_button.xo_color = XoColor('#010101,#a0a0a0') + self.reset_button.show() - self.attach(self._buttons[-1], i, 0, 1, 1) + def _reset_button_press_cb(self, widget, event): + self._set_gender('') + for i in range(len(GENDERS)): + self._buttons[i].xo_color = self._nocolor def _button_press_cb(self, widget, event, gender_index): if event.button == 1 and event.type == Gdk.EventType.BUTTON_PRESS: @@ -53,9 +89,10 @@ def _button_press_cb(self, widget, event, gender_index): self._buttons[1 - gender_index].xo_color = self._nocolor def get_gender(self): - return self._gender + return load_gender() def _set_gender(self, gender): + self.gender_changed_signal.emit(gender) self._gender = gender def update_color(self, color): diff --git a/src/jarabe/intro/window.py b/src/jarabe/intro/window.py index 72de64c490..69d6d37996 100644 --- a/src/jarabe/intro/window.py +++ b/src/jarabe/intro/window.py @@ -39,21 +39,6 @@ from jarabe.intro import colorpicker from jarabe.intro import genderpicker -_SECONDS_PER_YEAR = 365 * 24 * 60 * 60. - - -def calculate_birth_timestamp(age): - age_in_seconds = age * _SECONDS_PER_YEAR - birth_timestamp = int(time.time() - age_in_seconds) - return birth_timestamp - - -def calculate_age(birth_timestamp): - age_in_seconds = time.time() - birth_timestamp - # Round to nearest int - age = int(math.floor(age_in_seconds / _SECONDS_PER_YEAR) + 0.5) - return age - def create_profile_with_nickname(nickname): user_profile = UserProfile() @@ -64,6 +49,11 @@ def create_profile_with_nickname(nickname): def create_profile(user_profile): settings = Gio.Settings('org.sugarlabs.user') + if user_profile.nickname in [None, '']: + nick = settings.get_string('nick') + if nick is not None: + logging.debug('recovering old nickname %s' % (nick)) + user_profile.nickname = nick settings.set_string('nick', user_profile.nickname) colors = user_profile.colors @@ -71,14 +61,9 @@ def create_profile(user_profile): colors = XoColor() settings.set_string('color', colors.to_string()) - if user_profile.gender is not None: - settings.set_string('gender', user_profile.gender) - else: - settings.set_string('gender', '') + genderpicker.save_gender(user_profile.gender) - settings.set_int('birth-timestamp', - calculate_birth_timestamp(user_profile.age)) - # settings.sync() + agepicker.save_age(user_profile.age) # DEPRECATED from gi.repository import GConf @@ -88,11 +73,6 @@ def create_profile(user_profile): client.set_string('/desktop/sugar/user/color', colors.to_string()) - if user_profile.gender is not None: - client.set_string('/desktop/sugar/user/gender', user_profile.gender) - - client.set_int('/desktop/sugar/user/birth_timestamp', - calculate_birth_timestamp(user_profile.age)) client.suggest_sync() if profile.get_pubkey() and profile.get_profile().privkey_hash: @@ -253,11 +233,12 @@ def __init__(self, gender): grid.set_column_spacing(style.DEFAULT_SPACING) alignment.add(grid) - label = Gtk.Label(label=_('Select age:')) + self._ap = agepicker.AgePicker(gender, self) + + label = Gtk.Label(label=_(self._ap.get_label())) grid.attach(label, 0, 0, 1, 1) label.show() - self._ap = agepicker.AgePicker(gender) grid.attach(self._ap, 0, 1, 1, 1) self._ap.show() @@ -265,7 +246,6 @@ def __init__(self, gender): alignment.show() self._age = self._ap.get_age() - self.set_valid(True) def update_gender(self, gender): self._ap.update_gender(gender) @@ -288,7 +268,7 @@ class _IntroBox(Gtk.VBox): PAGE_FIRST = min(PAGE_NAME, PAGE_COLOR, PAGE_GENDER, PAGE_AGE) PAGE_LAST = max(PAGE_NAME, PAGE_COLOR, PAGE_GENDER, PAGE_AGE) - def __init__(self): + def __init__(self, start_on_age_page): Gtk.VBox.__init__(self) self.set_border_width(style.zoom(30)) @@ -303,7 +283,10 @@ def __init__(self): settings = Gio.Settings('org.sugarlabs.user') default_nick = settings.get_string('default-nick') if default_nick != 'disabled': - self._page = self.PAGE_COLOR + if start_on_age_page: + self._page = self.PAGE_AGE + else: + self._page = self.PAGE_COLOR if default_nick == 'system': pwd_entry = pwd.getpwuid(os.getuid()) default_nick = (pwd_entry.pw_gecos.split(',')[0] or @@ -343,6 +326,7 @@ def _setup_age_page(self): setup_methods[self._page](self) self.pack_start(self._current_page, True, True, 0) + self._current_page.show() button_box = Gtk.HButtonBox() if self._page == self.PAGE_FIRST: @@ -354,6 +338,7 @@ def _setup_age_page(self): back_button.set_image(image) back_button.connect('clicked', self._back_activated_cb) button_box.pack_start(back_button, True, True, 0) + back_button.show() self._next_button = Gtk.Button() image = Icon(icon_name='go-right') @@ -370,12 +355,14 @@ def _setup_age_page(self): self._update_next_button() button_box.pack_start(self._next_button, True, True, 0) + self._next_button.show() self._current_page.connect('notify::valid', self._page_valid_changed_cb) self.pack_start(button_box, False, True, 0) - self.show_all() + button_box.show() + # self.show_all() def _update_next_button(self): self._next_button.set_sensitive(self._current_page.props.valid) @@ -420,7 +407,7 @@ def __init__(self): self.nickname = None self.colors = None self.gender = None - self.age = 12 + self.age = 0 class IntroWindow(Gtk.Window): @@ -430,13 +417,13 @@ class IntroWindow(Gtk.Window): 'done': (GObject.SignalFlags.RUN_FIRST, None, ([])), } - def __init__(self): + def __init__(self, start_on_age_page=False): Gtk.Window.__init__(self) self.props.decorated = False self.maximize() - self._intro_box = _IntroBox() + self._intro_box = _IntroBox(start_on_age_page) self._intro_box.connect('done', self._done_cb) self.add(self._intro_box) diff --git a/src/jarabe/main.py b/src/jarabe/main.py index 433b360c79..6fcbaaa518 100755 --- a/src/jarabe/main.py +++ b/src/jarabe/main.py @@ -352,10 +352,10 @@ def setup_theme(): Gtk.IconTheme.get_default().append_search_path(icons_path) -def _start_intro(): - window = IntroWindow() +def _start_intro(start_on_age_page=False): + window = IntroWindow(start_on_age_page=start_on_age_page) window.connect('done', __intro_window_done_cb) - window.show_all() + window.show() def _check_profile(): @@ -370,6 +370,10 @@ def _check_profile(): return False +def _check_group_label(): + return intro.check_group_label() + + def main(): # This can be removed once pygobject-3.10 is a requirement. # https://bugzilla.gnome.org/show_bug.cgi?id=686914 @@ -399,6 +403,8 @@ def main(): if not _check_profile(): _start_intro() + elif not _check_group_label(): + _start_intro(start_on_age_page=True) else: _begin_desktop_startup() From 37d47d1535d38c901d30f607c19366d8961facad Mon Sep 17 00:00:00 2001 From: Martin Abente Lahaye Date: Thu, 25 Sep 2014 12:15:22 -0400 Subject: [PATCH 2/4] Fix age/gender INTRO widgets visibility Signed-off-by: Martin Abente Lahaye --- src/jarabe/intro/colorpicker.py | 1 + src/jarabe/intro/window.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/jarabe/intro/colorpicker.py b/src/jarabe/intro/colorpicker.py index 0ec8c00b4b..ca152fc3b2 100644 --- a/src/jarabe/intro/colorpicker.py +++ b/src/jarabe/intro/colorpicker.py @@ -32,6 +32,7 @@ def __init__(self): self._set_random_colors() self.connect('button-press-event', self._button_press_cb) self.add(self._xo) + self._xo.show() def _button_press_cb(self, widget, event): if event.button == 1 and event.type == Gdk.EventType.BUTTON_PRESS: diff --git a/src/jarabe/intro/window.py b/src/jarabe/intro/window.py index 69d6d37996..c391cfb42a 100644 --- a/src/jarabe/intro/window.py +++ b/src/jarabe/intro/window.py @@ -143,6 +143,7 @@ def __init__(self, intro): self._entry.set_size_request(style.zoom(300), -1) self._entry.set_max_length(45) grid.attach(self._entry, 0, 1, 1, 1) + self._entry.show() grid.show() alignment.show() @@ -362,7 +363,6 @@ def _setup_age_page(self): self.pack_start(button_box, False, True, 0) button_box.show() - # self.show_all() def _update_next_button(self): self._next_button.set_sensitive(self._current_page.props.valid) From 723499f812dca1eae920208cbc4719c140dc2560 Mon Sep 17 00:00:00 2001 From: Martin Abente Lahaye Date: Thu, 25 Sep 2014 12:33:28 -0400 Subject: [PATCH 3/4] Fix age/gender flawed default-nick logic Signed-off-by: Martin Abente Lahaye --- src/jarabe/intro/window.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/jarabe/intro/window.py b/src/jarabe/intro/window.py index c391cfb42a..36fb027513 100644 --- a/src/jarabe/intro/window.py +++ b/src/jarabe/intro/window.py @@ -284,16 +284,18 @@ def __init__(self, start_on_age_page): settings = Gio.Settings('org.sugarlabs.user') default_nick = settings.get_string('default-nick') if default_nick != 'disabled': - if start_on_age_page: - self._page = self.PAGE_AGE - else: - self._page = self.PAGE_COLOR + self._page = self.PAGE_COLOR if default_nick == 'system': pwd_entry = pwd.getpwuid(os.getuid()) default_nick = (pwd_entry.pw_gecos.split(',')[0] or pwd_entry.pw_name) self._name_page.set_name(default_nick) + # XXX should also consider whether or not there is a nick + nick = settings.get_string('nick') + if start_on_age_page and nick: + self._page = self.PAGE_AGE + self._setup_page() def _setup_page(self): From 497f426b8ccfee86ccc329022bb871e40c718cf0 Mon Sep 17 00:00:00 2001 From: Martin Abente Lahaye Date: Thu, 25 Sep 2014 12:42:53 -0400 Subject: [PATCH 4/4] Fix age/gender INTRO gender-selection and CP grade setup Signed-off-by: Martin Abente Lahaye --- extensions/cpsection/aboutme/view.py | 2 +- src/jarabe/intro/genderpicker.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/cpsection/aboutme/view.py b/extensions/cpsection/aboutme/view.py index 4c8d01c0f2..e4b95810b3 100644 --- a/extensions/cpsection/aboutme/view.py +++ b/extensions/cpsection/aboutme/view.py @@ -316,7 +316,7 @@ def _setup_age(self): grid.set_row_spacing(style.DEFAULT_SPACING) grid.set_column_spacing(style.DEFAULT_SPACING) - self._age_pickers = AgePicker(self._gender) + self._age_pickers = AgePicker(self._saved_gender) center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) center_in_panel.add(self._age_pickers) self._age_pickers.show() diff --git a/src/jarabe/intro/genderpicker.py b/src/jarabe/intro/genderpicker.py index dfd895caf3..3e8181e7cc 100644 --- a/src/jarabe/intro/genderpicker.py +++ b/src/jarabe/intro/genderpicker.py @@ -56,7 +56,7 @@ def __init__(self): self.set_row_spacing(style.DEFAULT_SPACING) self.set_column_spacing(style.DEFAULT_SPACING) - self._gender = self.get_gender() + self._gender = load_gender() self._buttons = [] self._nocolor = XoColor('#010101,#ffffff') self._color = XoColor() @@ -89,7 +89,7 @@ def _button_press_cb(self, widget, event, gender_index): self._buttons[1 - gender_index].xo_color = self._nocolor def get_gender(self): - return load_gender() + return self._gender def _set_gender(self, gender): self.gender_changed_signal.emit(gender)