Ejemplo n.º 1
0
    def setup_toolbar(self):
        toolbar_box = ToolbarBox()
        activity_button = ActivityButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        # Test Button
        video_toggle_button = ToolButton()
        video_toggle_button.connect("clicked", self.test_toggle)
        video_toggle_button.set_icon_name('activity-start')
        video_toggle_button.set_tooltip_text('Toggle Video Size')
        toolbar_box.toolbar.insert(video_toggle_button, 1)
        video_toggle_button.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()
        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()
        self.set_toolbar_box(toolbar_box)
        toolbar_box.show()
Ejemplo n.º 2
0
class ToolbarSpeed(Gtk.Toolbar):

    __gsignals__ = {
        "speed-changed": (GObject.SIGNAL_RUN_FIRST, None, [float]),
    }

    def __init__(self):
        Gtk.Toolbar.__init__(self)

        self.button = ToolbarButton(page=self, icon_name="media-playback-stop")
        self.playing = True
        self.speed = None

        self.stop_play = ToolButton("media-playback-stop")
        self.stop_play.set_tooltip(_("Stop"))
        self.stop_play.connect("clicked", self._stop_play)
        self.insert(self.stop_play, -1)

        self.slow_button = RadioToolButton(group=None,
                                           icon_name="slow-walk-milton-raposo")
        self.slow_button.set_tooltip(_("Run slow"))
        self.slow_button.connect("clicked", self._speed_changed_cb, Speed.SLOW)
        self.insert(self.slow_button, -1)

        self.normal_button = RadioToolButton(group=self.slow_button,
                                             icon_name="walking")
        self.normal_button.set_tooltip(_("Run Normal"))
        self.normal_button.connect("clicked", self._speed_changed_cb,
                                   Speed.NORMAL)
        self.insert(self.normal_button, -1)

        self.fast_button = RadioToolButton(group=self.slow_button,
                                           icon_name="running")
        self.fast_button.set_tooltip(_("Run fast"))
        self.fast_button.connect("clicked", self._speed_changed_cb, Speed.FAST)
        self.insert(self.fast_button, -1)

        self.slow_button.set_active(True)

        self.show_all()

    def _stop_play(self, button=None):
        self.playing = not self.playing

        if self.playing:
            self.stop_play.set_icon_name("media-playback-stop")
            self.stop_play.set_tooltip(_("Stop"))
            self.button.set_icon_name("media-playback-stop")

            if self.speed is None:
                self.speed = Speed.SLOW

            self.emit("speed-changed", self.speed)

        else:
            self.stop_play.set_icon_name("media-playback-start")
            self.stop_play.set_tooltip(_("Start"))
            self.button.set_icon_name("media-playback-start")

            self.emit("speed-changed", Speed.STOPPED)

    def _speed_changed_cb(self, button, speed):
        if not self.playing:
            self.speed = speed
            self._stop_play()

        else:
            self.emit("speed-changed", speed)
            self.speed = speed

    def enable(self):
        self.set_sensitive_buttons(True)

    def disable(self):
        self.set_sensitive_buttons(False)

    def set_sensitive_buttons(self, sensitive):
        self.stop_play.set_sensitive(sensitive)
        self.slow_button.set_sensitive(sensitive)
        self.normal_button.set_sensitive(sensitive)
        self.fast_button.set_sensitive(sensitive)
Ejemplo n.º 3
0
class SensorToolbar(Gtk.Toolbar):
    ''' The toolbar for specifiying the sensor: sound, resitance, or
    voltage '''

    LOWER = 0.0
    UPPER = 1.0
    STR_DC_R = \
        _("Resistive sensor (connect sensor to pink 'Mic In' on left side \
of XO)"       ) + ' '
    STR_DC_V = \
        _("Voltage sensor (connect sensor to pink 'Mic In' on left side \
of XO)"       ) + ' '
    STR_AC = _('Sound') + ' '
    STR_RESISTANCE = _('Resistance') + ' (' + _('Ohms') + ') '
    STR_VOLTAGE = _('Voltage') + ' (' + _('Volts') + ') '
    STR_TIME = _('Time Base') + ' '
    STR_FREQUENCY = _('Frequency Base') + ' '
    STR_INVERT = ' ' + _('Invert') + ' '
    STR_XAXIS_TEXT = _('X Axis Scale: 1 division = %(division)s %(unit)s')
    # TRANSLATORS: This is milli seconds.
    MS = _('ms')
    # TRANSLATORS: This is Hertz, so 1/second.
    HZ = _('Hz')

    def __init__(self, activity, channels):
        ''' By default, start with resistance mode '''

        super(type(self), self).__init__()

        self.activity = activity
        self._channels = channels
        self._lock_radio_buttons = False
        self._radio_button_pushed = False
        self.values = []
        for i in range(self._channels):
            self.values.append('')

        self.string_for_textbox = ''

        self.gain = 1.0
        self.y_mag = 3.0
        self.capture_gain = CAPTURE_GAIN
        self.mic_boost = MIC_BOOST

        self.mode = 'sound'

        # Set up Time-domain Button
        self.time = RadioToolButton(icon_name='media-audio', group=None)
        self.insert(self.time, -1)
        self.time.set_tooltip(_('Sound'))
        self.time.connect('clicked', self.analog_resistance_voltage_mode_cb,
                          'sound')

        # Set up Resistance Button
        self.resistance = RadioToolButton(icon_name='resistance',
                                          group=self.time)
        if _can_use_dc(self.activity.hw):
            self.insert(self.resistance, -1)
        self.resistance.show()
        self.resistance.set_tooltip(_('Resistance Sensor'))
        self.resistance.connect('clicked',
                                self.analog_resistance_voltage_mode_cb,
                                'resistance')

        # Set up Voltage Button
        self.voltage = RadioToolButton(icon_name='voltage', group=self.time)
        if _can_use_dc(self.activity.hw):
            self.insert(self.voltage, -1)
        self.voltage.set_tooltip(_('Voltage Sensor'))
        self.voltage.connect('clicked', self.analog_resistance_voltage_mode_cb,
                             'voltage')

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = True
        self.insert(separator, -1)

        self._log_value = LOG_TIMER_VALUES[1]
        self.log_label = Gtk.Label(self._log_to_string(self._log_value))
        toolitem = Gtk.ToolItem()
        toolitem.add(self.log_label)
        self.insert(toolitem, -1)

        self._log_button = ToolButton('timer-10')
        self._log_button.set_tooltip(_('Select logging interval'))
        self._log_button.connect('clicked', self._log_selection_cb)
        self.insert(self._log_button, -1)
        self._setup_log_palette()

        # Set up Logging/Stop Logging Button
        self._record = ToolButton('media-record')
        self.insert(self._record, -1)
        self._record.set_tooltip(_('Start logging'))
        self._record.connect('clicked', self.record_control_cb)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = True
        self.insert(separator, -1)

        toolitem = Gtk.ToolItem()
        self.trigger_label = Gtk.Label(_('Trigger'))
        toolitem.add(self.trigger_label)
        self.insert(toolitem, -1)

        # Set up Trigger Combo box
        self.trigger_none = RadioToolButton()
        self.trigger_none.set_icon_name('trigger-none')
        self.insert(self.trigger_none, -1)
        self.trigger_none.set_tooltip(_('None'))
        self.trigger_none.connect('clicked', self.update_trigger_control_cb,
                                  self.activity.wave.TRIGGER_NONE)

        self.trigger_rise = RadioToolButton(group=self.trigger_none)
        self.trigger_rise.set_icon_name('trigger-rise')
        self.insert(self.trigger_rise, -1)
        self.trigger_rise.set_tooltip(_('Rising Edge'))
        self.trigger_rise.connect('clicked', self.update_trigger_control_cb,
                                  self.activity.wave.TRIGGER_POS)

        self.trigger_fall = RadioToolButton(group=self.trigger_none)
        self.trigger_fall.set_icon_name('trigger-fall')
        self.insert(self.trigger_fall, -1)
        self.trigger_fall.set_tooltip(_('Falling Edge'))
        self.trigger_fall.connect('clicked', self.update_trigger_control_cb,
                                  self.activity.wave.TRIGGER_NEG)

        self.show_all()

    def get_log(self):
        return self._log_value

    def get_log_idx(self):
        if self._log_value in LOG_TIMER_VALUES:
            return LOG_TIMER_VALUES.index(self._log_value)
        else:
            return LOG_TIMER_VALUES[0]

    def set_log_idx(self, idx):
        self._log_value = LOG_TIMER_VALUES[idx]
        self.log_label.set_text(self._log_to_string(self._log_value))
        if hasattr(self, '_log_button'):
            self._log_button.set_icon('timer-%d' % (self._log_value))

    def _log_selection_cb(self, widget):
        if self._log_palette:
            if not self._log_palette.is_up():
                self._log_palette.popup(immediate=True)
            else:
                self._log_palette.popdown(immediate=True)
            return

    def _log_to_seconds(self, tenth_seconds):
        return tenth_seconds / 10.

    def _log_to_string(self, tenth_seconds):
        if tenth_seconds in LOG_TIMER_LABELS:
            return LOG_TIMER_LABELS[tenth_seconds]
        else:
            return _('1 second')

    def _setup_log_palette(self):
        self._log_palette = self._log_button.get_palette()

        for tenth_seconds in LOG_TIMER_VALUES:
            text = self._log_to_string(tenth_seconds)
            menu_item = MenuItem(icon_name='timer-%d' % (tenth_seconds),
                                 text_label=text)
            menu_item.connect('activate', self._log_selected_cb, tenth_seconds)
            self._log_palette.menu.append(menu_item)
            menu_item.show()

    def _log_selected_cb(self, button, seconds):
        self.set_log_idx(LOG_TIMER_VALUES.index(seconds))

    def add_frequency_slider(self, toolbox):
        ''' Either on the Sound toolbar or the Main toolbar '''
        self._freq_stepper_up = ToolButton('freq-high')
        self._freq_stepper_up.set_tooltip(_('Zoom out'))
        self._freq_stepper_up.connect('clicked', self._freq_stepper_up_cb)
        self._freq_stepper_up.show()
        self.activity.adjustmentf = Gtk.Adjustment(0.5, self.LOWER, self.UPPER,
                                                   0.01, 0.1, 0)
        self.activity.adjustmentf.connect('value_changed', self.cb_page_sizef)
        self._freq_range = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL,
                                     adjustment=self.activity.adjustmentf)
        self._freq_range.set_inverted(True)
        self._freq_range.set_draw_value(False)
        self._freq_range.set_size_request(120, 15)
        self._freq_range.show()

        self._freq_stepper_down = ToolButton('freq-low')
        self._freq_stepper_down.set_tooltip(_('Zoom in'))
        self._freq_stepper_down.connect('clicked', self._freq_stepper_down_cb)
        self._freq_stepper_down.show()

        self._freq_range_tool = Gtk.ToolItem()
        self._freq_range_tool.add(self._freq_range)
        self._freq_range_tool.show()

        toolbox.add(self._freq_stepper_up)
        toolbox.add(self._freq_range_tool)
        toolbox.add(self._freq_stepper_down)
        return

    def update_trigger_control_cb(self, button, value):
        if button is None:
            value = self.activity.wave.TRIGGER_NONE
        if self.activity.wave.get_fft_mode():
            self.trigger_none.set_active(True)
        else:
            self.activity.wave.set_trigger(value)

    def analog_resistance_voltage_mode_cb(self,
                                          button=None,
                                          mode_to_set='sound'):
        ''' Callback for Analog/Resistance/Voltage Buttons '''
        if self._lock_radio_buttons:
            logging.debug('mode selector locked')
            self._radio_button_pushed = True
            return
        if self.mode == mode_to_set:
            logging.debug('mode already set to %s' % mode_to_set)
            return
        self._lock_radio_buttons = True
        if self.activity.CONTEXT == 'sound':
            self.sound_context_off()
        else:
            self.sensor_context_off()

        # Force time domain when switching modes
        if self.activity.wave.get_fft_mode():
            self.activity.timefreq_control()
        # Turn off logging when switching modes
        if self.activity.audiograb.we_are_logging:
            self.record_control_cb()

        self.set_mode(mode_to_set)
        if mode_to_set == 'sound':
            self.set_sound_context()
        elif mode_to_set == 'resistance':
            self.set_sensor_context()
        elif mode_to_set == 'voltage':
            self.set_sensor_context()
        self.update_string_for_textbox()
        return False

    def unlock_radio_buttons(self):
        ''' Enable radio button selection '''
        logging.debug('unlocking radio buttons')
        if self._radio_button_pushed:
            if self.mode == 'sound':
                self.time.set_active(True)
            elif self.mode == 'resistance':
                self.resistance.set_active(True)
            elif self.mode == 'voltage':
                self.voltage.set_active(True)
        self._lock_radio_buttons = False
        self._radio_button_pushed = False

    def set_mode(self, mode='sound'):
        ''' Set the mixer settings to match the current mode. '''
        self.mode = mode
        self.activity.audiograb.set_sensor_type(self.mode)
        for i in range(self._channels):
            self.values[i] = 0.0
        return

    def get_mode(self):
        ''' Get the mixer settings. '''
        return self.mode

    def _freq_stepper_up_cb(self, button=None):
        ''' Moves the horizontal zoom slider to the left one notch,
        where one notch is 1/100 of the total range. This correspond
        to zooming out as a larger number of Hertz or milliseconds
        will be represented by the same space on the screen. '''
        new_value = self._freq_range.get_value() + \
            (self.UPPER - self.LOWER) / 100.0
        if new_value <= self.UPPER:
            self._freq_range.set_value(new_value)
        else:
            self._freq_range.set_value(self.UPPER)

    def _freq_stepper_down_cb(self, button=None):
        ''' Moves the horizontal zoom slider to the right one notch,
        where one notch is 1/100 of the total range. This corresponds
        to zooming in. '''
        new_value = self._freq_range.get_value() - \
            (self.UPPER - self.LOWER) / 100.0
        if new_value >= self.LOWER:
            self._freq_range.set_value(new_value)
        else:
            self._freq_range.set_value(self.LOWER)

    def cb_page_sizef(self, button=None):
        ''' Callback to scale the frequency range (zoom in and out) '''
        if self._update_page_size_id:
            GObject.source_remove(self._update_page_size_id)
        self._update_page_size_id =\
            GObject.timeout_add(250, self.update_page_size)
        return True

    def update_page_size(self):
        ''' Set up the scaling of the display. '''
        self._update_page_size_id = None
        value = self.activity.adjustmentf.get_value()
        new_value = round(value * 100.0) / 100.0
        if value != new_value:
            self.activity.adjustmentf.set_value(new_value)
            return False
        time_div = 0.001 * max(value, 0.05)
        freq_div = 1000 * max(value, 0.01)
        self.activity.wave.set_div(time_div, freq_div)
        self.update_string_for_textbox()
        return False

    def set_sound_context(self):
        ''' Called when analog sensing is selected '''
        self.set_show_hide_windows(mode='sound')
        GObject.timeout_add(500, self.sound_context_on)
        self.activity.CONTEXT = 'sound'

    def set_sensor_context(self):
        ''' Called when digital sensing is selected '''
        self.set_show_hide_windows(mode='sensor')
        GObject.timeout_add(500, self.sensor_context_on)
        self.activity.CONTEXT = 'sensor'

    def set_show_hide_windows(self, mode='sound'):
        ''' Shows the appropriate window identified by the mode '''
        self.activity.wave.set_context_on()
        for i in range(self._channels):
            self.activity.side_toolbars[i].set_show_hide(True, mode)

    def sensor_context_off(self):
        ''' Called when a DC sensor is no longer selected '''
        # self.activity.audiograb.pause_grabbing()
        self.activity.audiograb.stop_grabbing()

    def sensor_context_on(self):
        ''' Called when a DC sensor is selected '''
        self.update_string_for_textbox()
        self.activity.wave.set_trigger(self.activity.wave.TRIGGER_NONE)
        # self.activity.audiograb.resume_grabbing()
        self.activity.audiograb.start_grabbing()
        return False

    def sound_context_off(self):
        ''' Called when an analog sensor is no longer selected '''
        self.gain, self.y_mag = self.activity.wave.get_mag_params()
        self.capture_gain = self.activity.audiograb.get_capture_gain()
        self.mic_boost = self.activity.audiograb.get_mic_boost()
        self.activity.audiograb.stop_grabbing()

    def sound_context_on(self):
        ''' Called when an analog sensor is selected '''
        self.activity.wave.set_mag_params(self.gain, self.y_mag)
        self.update_string_for_textbox()
        self.update_trigger_control_cb(None, self.activity.wave.TRIGGER_NONE)
        self.activity.audiograb.start_grabbing()
        return False

    def set_sample_value(self, value='', channel=0):
        ''' Write a sample value to the textbox. '''
        self.values[channel] = value
        self.update_string_for_textbox()
        return

    def record_control_cb(self, button=None):
        ''' Depending upon the selected interval, does either a logging
        session, or just logs the current buffer. '''
        if self.activity.audiograb.we_are_logging:
            self.activity.audiograb.set_logging_params(start_stop=False)
            self._record.set_icon_name('media-record')
            self._record.show()
            self._record.set_tooltip(_('Start Recording'))
        else:
            Xscale = (1.00 / self.activity.audiograb.get_sampling_rate())
            Yscale = 0.0
            interval = self._log_value / 10.  # self.interval_convert()
            username = self.activity.nick
            if self.activity.wave.get_fft_mode():
                self.activity.data_logger.start_new_session(
                    username,
                    Xscale,
                    Yscale,
                    self._log_to_string(self._log_value),
                    channels=self._channels,
                    mode='frequency')
            else:
                self.activity.data_logger.start_new_session(
                    username,
                    Xscale,
                    Yscale,
                    self._log_to_string(self._log_value),
                    channels=self._channels,
                    mode=self.mode)
            self.activity.audiograb.set_logging_params(start_stop=True,
                                                       interval=interval)
            self._record.set_icon_name('record-stop')
            self._record.show()
            self._record.set_tooltip(_('Stop Recording'))
            self.activity.new_recording = True

    def update_string_for_textbox(self):
        ''' Update the status field at the bottom of the canvas. '''
        if self.mode == 'resistance':
            string_for_textbox = (self.STR_DC_R + '\n')
            string_for_textbox += self.STR_RESISTANCE
        elif self.mode == 'voltage':
            string_for_textbox = (self.STR_DC_V + '\n')
            string_for_textbox += self.STR_VOLTAGE
        else:
            string_for_textbox = (self.STR_AC + '\t')
        if self.activity.wave.get_fft_mode():
            scalex = self.STR_XAXIS_TEXT % {
                'unit': self.HZ,
                'division': self.activity.wave.freq_div
            }
            string_for_textbox += self.STR_FREQUENCY
            string_for_textbox += ('\n' + scalex)
        elif self.mode == 'sound':
            scalex = self.STR_XAXIS_TEXT % {
                'unit': self.MS,
                'division': self.activity.wave.time_div * 1000
            }
            string_for_textbox += self.STR_TIME
            string_for_textbox += ('\n' + scalex)
        else:
            for i in range(self._channels):
                string_for_textbox += '\t(%s)' % (self.values[i])
        invert = False
        for i in range(self._channels):
            if self.activity.wave.get_invert_state(channel=i):
                invert = True
        if invert:
            string_for_textbox += self.STR_INVERT
        self.activity.text_box.set_label(string_for_textbox)
Ejemplo n.º 4
0
class DevelopViewToolbar(Gtk.Toolbar):
    __gsignals__ = {
        'theme-changed': (GObject.SIGNAL_RUN_FIRST, None,
                         (str,)),
        'font-size-changed': (GObject.SIGNAL_RUN_FIRST, None,
                             (int,)),
    }

    def __init__(self, _activity):
        GObject.GObject.__init__(self)

        self._activity = _activity
        self.theme_state = "light"
        self.font_size = DEFAULT_FONT_SIZE

        self.theme_toggler = ToolButton('dark-theme')
        self.theme_toggler.connect('clicked', self._toggled_theme)
        self.theme_toggler.set_tooltip('Switch to Dark Theme')
        self.insert(self.theme_toggler, -1)
        self.theme_toggler.show()

        sep = Gtk.SeparatorToolItem()
        self.insert(sep, -1)
        sep.show()

        self.font_plus = ToolButton('zoom-in')
        self.font_plus.connect('clicked', self._font_size_increase)
        self.font_plus.set_tooltip('Zoom out')
        self.insert(self.font_plus, -1)
        self.font_plus.show()

        self.font_minus = ToolButton('zoom-out')
        self.font_minus.connect('clicked', self._font_size_decrease)
        self.font_minus.set_tooltip('Zoom in')
        self.insert(self.font_minus, -1)
        self.font_minus.show()

        self.show()

    def set_theme(self, theme_name):
        self.theme_state = theme_name

        if self.theme_state == "light":
            self.theme_toggler.set_icon_name('dark-theme')
            self.theme_toggler.set_tooltip('Switch to Dark Theme')
        elif self.theme_state == "dark":
            self.theme_toggler.set_icon_name('light-theme')
            self.theme_toggler.set_tooltip('Switch to Light Theme')

        self.emit('theme-changed', self.theme_state)

    def set_font_size(self, font_size):
        self.font_size = font_size
        self.emit('font-size-changed', self.font_size)

    def _font_size_increase(self, button):
        self.font_size += FONT_CHANGE_STEP
        self.emit('font-size-changed', self.font_size)

    def _font_size_decrease(self, button):
        self.font_size -= FONT_CHANGE_STEP
        self.emit('font-size-changed', self.font_size)

    def _toggled_theme(self, button):
        if self.theme_state == "dark":
            self.theme_state = "light"
            self.theme_toggler.set_icon_name('dark-theme')
            self.theme_toggler.set_tooltip('Switch to Dark Theme')
        elif self.theme_state == "light":
            self.theme_state = "dark"
            self.theme_toggler.set_icon_name('light-theme')
            self.theme_toggler.set_tooltip('Switch to Light Theme')

        self.emit('theme-changed', self.theme_state)
Ejemplo n.º 5
0
class PhysicsActivity(activity.Activity):

    def __init__(self, handle):
        super(PhysicsActivity, self).__init__(handle)
        self._collab = CollabWrapper(self)
        self._collab.message.connect(self.__message_cb)
        self.metadata['mime_type'] = 'application/x-physics-activity'
        self.add_events(Gdk.EventMask.ALL_EVENTS_MASK |
                        Gdk.EventMask.VISIBILITY_NOTIFY_MASK)

        self.connect('visibility-notify-event', self._focus_event)
        self.connect('window-state-event', self._window_event)

        self.game = PhysicsGame(self)
        self.game.canvas = sugargame.canvas.PygameCanvas(
            self, main=self.game.run, modules=[pygame.display, pygame.font])

        self.preview = None
        self._sample_window = None

        self._notebook = Gtk.Notebook(show_tabs=False)
        self._notebook.add(self.game.canvas)

        w = Gdk.Screen.width()
        h = Gdk.Screen.height() - 2 * GRID_CELL_SIZE

        self.game.canvas.set_size_request(w, h)

        self._constructors = {}
        self.build_toolbar()

        self.set_canvas(self._notebook)
        Gdk.Screen.get_default().connect('size-changed',
                                         self.__configure_cb)

        self.show_all()
        self._collab.setup()

    def get_data(self):
        """ FIXME: not implemented, thus objects created before
        sharing starts are not shared """
        return dict()

    def set_data(self, data):
        pass

    def __configure_cb(self, event):
        ''' Screen size has changed '''
        self.write_file(os.path.join(
                        activity.get_activity_root(), 'data', 'data'))
        w = Gdk.Screen.width()
        h = Gdk.Screen.height() - 2 * GRID_CELL_SIZE
        pygame.display.set_mode((w, h),
                                pygame.RESIZABLE)
        self.read_file(os.path.join(
                       activity.get_activity_root(), 'data', 'data'))

    def read_file(self, file_path):
        self.game.read_file(file_path)

    def write_file(self, file_path):
        self.game.write_file(file_path)

    def get_preview(self):
        ''' Custom preview code to get image from pygame. '''
        return self.game.canvas.get_preview()

    def build_toolbar(self):
        self.max_participants = 4

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        create_toolbar = ToolbarButton()
        create_toolbar.props.page = Gtk.Toolbar()
        create_toolbar.props.icon_name = 'magicpen'
        create_toolbar.props.label = _('Create')
        toolbar_box.toolbar.insert(create_toolbar, -1)
        self._insert_create_tools(create_toolbar)

        color = ColorToolButton('color')
        color.connect('notify::color', self.__color_notify_cb)
        toolbar_box.toolbar.insert(color, -1)
        color.show()

        random = ToggleToolButton('colorRandom')
        random.set_tooltip(_('Toggle random color'))
        toolbar_box.toolbar.insert(random, -1)
        random.set_active(True)
        random.connect('toggled', self.__random_toggled_cb)
        random.show()

        color.random = random
        random.color = color

        random.timeout_id = GLib.timeout_add(100, self.__timeout_cb, random)

        self._insert_stop_play_button(toolbar_box.toolbar)

        clear_trace = ToolButton('clear-trace')
        clear_trace.set_tooltip(_('Clear Trace Marks'))
        clear_trace.set_accelerator(_('<ctrl>x'))
        clear_trace.connect('clicked', self.clear_trace_cb)
        clear_trace.set_sensitive(False)
        toolbar_box.toolbar.insert(clear_trace, -1)
        clear_trace.show()
        self.clear_trace = clear_trace

        self._insert_clear_all_button(toolbar_box.toolbar)

        load_example = ToolButton('load-sample')
        load_example.set_tooltip(_('Show sample projects'))
        load_example.connect('clicked', self._create_store)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        toolbar_box.toolbar.insert(load_example, -1)
        load_example.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_size_request(0, -1)
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop = StopButton(self)
        toolbar_box.toolbar.insert(stop, -1)
        stop.show()

        separator = Gtk.SeparatorToolItem()
        activity_button.props.page.insert(separator, -1)
        separator.show()

        export_json = ToolButton('save-as-json')
        export_json.set_tooltip(_('Export tracked objects to journal'))
        export_json.connect('clicked', self._export_json_cb)
        activity_button.props.page.insert(export_json, -1)
        export_json.show()

        load_project = ToolButton('load-project')
        load_project.set_tooltip(_('Load project from journal'))
        load_project.connect('clicked', self._load_project)
        activity_button.props.page.insert(load_project, -1)
        load_project.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()
        create_toolbar.set_expanded(True)
        return toolbar_box

    def __color_notify_cb(self, button, pdesc):
        """ when a color is chosen;
        change world object add color,
        and change color of buttons. """

        color = button.get_color()
        self._set_color(color)
        button.random.set_active(False)
        button.random.get_icon_widget().set_stroke_color(self._rgb8x(color))

    def __random_toggled_cb(self, random):
        if random.props.active:
            self._random_on(random)
        else:
            self._random_off(random)

    def _random_on(self, random):
        """ when random is turned on;
        reset world object add color,
        and begin watching for changed world object add color. """

        self.game.world.add.reset_color()

        if random.timeout_id is None:
            random.timeout_id = GLib.timeout_add(100, self.__timeout_cb,
                                                 random)
            self.__timeout_cb(random)

    def _random_off(self, random):
        """ when random is turned off;
        change world object add color back to chosen color,
        change color of buttons,
        and stop watching for changed world object add color. """

        color = random.color.get_color()
        self._set_color(color)
        random.get_icon_widget().set_stroke_color(self._rgb8x(color))

        if random.timeout_id is not None:
            GLib.source_remove(random.timeout_id)
            random.timeout_id = None

    def __timeout_cb(self, random):
        """ copy the next color to the random button stroke color. """
        if hasattr(self.game, "world"):
            color = self.game.world.add.next_color()
            random.get_icon_widget().set_stroke_color('#%.2X%.2X%.2X' % color)
        return True

    def _set_color(self, color):
        """ set world object add color. """
        self.game.world.add.set_color(self._rgb8(color))

    def _rgb8x(self, color):
        """ convert a Gdk.Color into hex triplet. """
        return '#%.2X%.2X%.2X' % self._rgb8(color)

    def _rgb8(self, color):
        """ convert a Gdk.Color into an 8-bit RGB tuple. """
        return (color.red / 256, color.green / 256, color.blue / 256)

    def can_close(self):
        self.preview = self.get_preview()
        self.game.running = False
        return True

    def _insert_stop_play_button(self, toolbar):

        self.stop_play_toolbar = ToolbarButton()
        st_toolbar = self.stop_play_toolbar
        st_toolbar.props.page = Gtk.Toolbar()
        st_toolbar.props.icon_name = 'media-playback-stop'

        self.stop_play_state = True
        self.stop_play = ToolButton('media-playback-stop')
        self.stop_play.set_tooltip(_('Stop'))
        self.stop_play.set_accelerator(_('<ctrl>space'))
        self.stop_play.connect('clicked', self.stop_play_cb)
        self._insert_item(st_toolbar, self.stop_play)
        self.stop_play.show()

        slowest_button = RadioToolButton(group=None)
        slowest_button.set_icon_name('slow-walk-milton-raposo')
        slowest_button.set_tooltip(_('Run slower'))
        slowest_button.connect('clicked', self._set_fps_cb, SLOWEST_FPS)
        self._insert_item(st_toolbar, slowest_button)
        slowest_button.show()

        slow_button = RadioToolButton(group=slowest_button)
        slow_button.set_icon_name('walking')
        slow_button.set_tooltip(_('Run slow'))
        slow_button.connect('clicked', self._set_fps_cb, SLOW_FPS)
        self._insert_item(st_toolbar, slow_button)
        slow_button.show()

        fast_button = RadioToolButton(group=slowest_button)
        fast_button.set_icon_name('running')
        fast_button.set_tooltip('Run fast')
        fast_button.connect('clicked', self._set_fps_cb, FAST_FPS)
        self._insert_item(st_toolbar, fast_button)
        fast_button.show()
        fast_button.set_active(True)

        toolbar.insert(self.stop_play_toolbar, -1)
        self.stop_play_toolbar.show_all()

    def _set_fps_cb(self, button, value):
        self.game.set_game_fps(value)

    def _insert_clear_all_button(self, toolbar):
        self.clear_all = ToolButton('clear_all')
        self.clear_all.set_tooltip(_('Erase All'))
        self.clear_all.set_accelerator(_('<ctrl>a'))
        self.clear_all.connect('clicked', self.clear_all_cb)
        toolbar.insert(self.clear_all, -1)
        self.clear_all.set_sensitive(False)
        self.clear_all.show()

    def _insert_item(self, toolbar, item, pos=-1):
        if hasattr(toolbar, 'insert'):
            toolbar.insert(item, pos)
        else:
            toolbar.props.page.insert(item, pos)

    def _insert_create_tools(self, create_toolbar):
        # Make + add the component buttons
        self.radioList = {}
        for i, c in enumerate(tools.allTools):
            if i == 0:
                button = RadioToolButton(group=None)
                firstbutton = button
            else:
                button = RadioToolButton(group=firstbutton)
            button.set_icon_name(c.icon)
            button.set_tooltip(c.toolTip)
            button.set_accelerator(c.toolAccelerator)
            button.connect('clicked', self.radioClicked)
            palette = self._build_palette(c)
            if palette is not None:
                palette.show()
                button.get_palette().set_content(palette)
            self._insert_item(create_toolbar, button, -1)
            button.show()
            self.radioList[button] = c.name
            if hasattr(c, 'constructor'):
                self._constructors[c.name] = \
                    self.game.toolList[c.name].constructor

    def __icon_path(self, name):
        activity_path = activity.get_bundle_path()
        icon_path = os.path.join(activity_path, 'icons',
                                 name + '.svg')
        return icon_path

    def _build_palette(self, tool):
        if tool.palette_enabled:
            if tool.palette_mode == tools.PALETTE_MODE_ICONS:
                grid = Gtk.Grid()
                for s, settings in enumerate(tool.palette_settings):
                    self.game.toolList[tool.name].buttons.append([])
                    for i, icon_value in enumerate(settings['icon_values']):
                        if i == 0:
                            button = RadioToolButton(group=None)
                            firstbutton = button
                        else:
                            button = RadioToolButton(group=firstbutton)
                        button.set_icon_name(settings['icons'][i])
                        button.connect('clicked',
                                       self._palette_icon_clicked,
                                       tool.name,
                                       s,
                                       settings['name'],
                                       icon_value)
                        grid.attach(button, i, s, 1, 1)
                        self.game.toolList[tool.name].buttons[s].append(button)
                        button.show()
                        if settings['active'] == settings['icons'][i]:
                            button.set_icon_name(settings['icons'][i] +
                                                 '-selected')
                            button.set_active(True)
                return grid
        else:
            return None

    def _palette_icon_clicked(self, widget, toolname, s, value_name, value):
        for tool in tools.allTools:
            if tool.name == toolname:
                # Radio buttons are not highlighting in the palette
                # so adding highlight by hand
                # See http://bugs.sugarlabs.org/ticket/305
                setting = self.game.toolList[tool.name].palette_settings[s]
                for i, button in enumerate(
                        self.game.toolList[tool.name].buttons[s]):
                    icon_name = setting['icons'][i]
                    if button == widget:
                        button.set_icon_name(icon_name + '-selected')
                    else:
                        button.set_icon_name(icon_name)
                if hasattr(tool, 'palette_data_type'):
                    tool.palette_data_type = value
                else:
                    tool.palette_data[value_name] = value

    def clear_trace_alert_cb(self, alert, response):
        self.remove_alert(alert)
        if response is Gtk.ResponseType.OK:
            self.game.full_pos_list = [[] for _ in self.game.full_pos_list]
            self.game.tracked_bodies = 0

    def clear_trace_cb(self, button):
        clear_trace_alert = ConfirmationAlert()
        clear_trace_alert.props.title = _('Are You Sure?')
        clear_trace_alert.props.msg = \
            _('All trace points will be erased. This cannot be undone!')
        clear_trace_alert.connect('response', self.clear_trace_alert_cb)
        self.add_alert(clear_trace_alert)

    def stop_play_cb(self, button):
        pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                             action='stop_start_toggle'))
        self.stop_play_state = not self.stop_play_state

        if self.stop_play_state:
            self.stop_play.set_icon_name('media-playback-stop')
            self.stop_play.set_tooltip(_('Stop'))

            self.stop_play_toolbar.set_icon_name('media-playback-stop')
        else:
            self.stop_play.set_icon_name('media-playback-start')
            self.stop_play.set_tooltip(_('Start'))

            self.stop_play_toolbar.set_icon_name('media-playback-start')

    def clear_all_cb(self, button):
        def clear_all_alert_cb(alert, response_id):
            self.remove_alert(alert)
            if response_id is Gtk.ResponseType.OK:
                pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                     action='clear_all'))
        if len(self.game.world.world.bodies) > 2:
            clear_all_alert = ConfirmationAlert()
            clear_all_alert.props.title = _('Are You Sure?')
            clear_all_alert.props.msg = \
                _('All your work will be discarded. This cannot be undone!')
            clear_all_alert.connect('response', clear_all_alert_cb)
            self.add_alert(clear_all_alert)

    def radioClicked(self, button):
        pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                             action=self.radioList[button]))

    def _focus_event(self, event, data=None):
        ''' Send focus events to pygame to allow it to idle when in
        background. '''
        if data.state == Gdk.VisibilityState.FULLY_OBSCURED:
            pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                 action='focus_out'))
        else:
            self.game.show_fake_cursor = True
            pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                 action='focus_in'))

    def _export_json_cb(self, button):
        jobject = datastore.create()
        jobject.metadata['title'] = _('Physics export')
        jobject.metadata['mime_type'] = 'text/plain'

        tmp_dir = os.path.join(self.get_activity_root(), 'instance')
        fd, file_path = tempfile.mkstemp(dir=tmp_dir)
        os.close(fd)

        self.game.world.json_save(file_path)

        jobject.set_file_path(file_path)
        datastore.write(jobject)

    def _window_event(self, window, event):
        ''' Send focus out event to pygame when switching to a desktop
        view. '''
        if event.changed_mask & Gdk.WindowState.ICONIFIED:
            pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                 action='focus_out'))

    def _restore_cursor(self):
        ''' No longer waiting, so restore standard cursor. '''
        if not hasattr(self, 'get_window'):
            return
        self.get_window().set_cursor(self.old_cursor)

    def _waiting_cursor(self):
        ''' Waiting, so set watch cursor. '''
        if not hasattr(self, 'get_window'):
            return
        self.old_cursor = self.get_window().get_cursor()
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))

    def __message_cb(self, collab, buddy, msg):
        ''' Data is passed as tuples: cmd:text '''
        action = msg.get('action')
        if action != 'text':
            return

        text = msg['text']
        dispatch_table = {'C': self._construct_shared_circle,
                          'B': self._construct_shared_box,
                          'T': self._construct_shared_triangle,
                          'P': self._construct_shared_polygon,
                          'M': self._construct_shared_magicpen,
                          'j': self._add_shared_joint,
                          'p': self._add_shared_pin,
                          'm': self._add_shared_motor,
                          't': self._add_shared_track,
                          'c': self._add_shared_chain,
                          }
        logging.debug('<<< %s' % (text[0]))
        dispatch_table[text[0]](text[2:])

    def _construct_shared_circle(self, data):
        circle_data = json.loads(data)
        pos = circle_data[0]
        radius = circle_data[1]
        density = circle_data[2]
        restitution = circle_data[3]
        friction = circle_data[4]
        self._constructors['Circle'](pos, radius, density, restitution,
                                     friction, share=False)

    def _construct_shared_box(self, data):
        box_data = json.loads(data)
        pos1 = box_data[0]
        pos2 = box_data[1]
        density = box_data[2]
        restitution = box_data[3]
        friction = box_data[4]
        self._constructors['Box'](pos1, pos2, density, restitution,
                                  friction, share=False)

    def _construct_shared_triangle(self, data):
        triangle_data = json.loads(data)
        pos1 = triangle_data[0]
        pos2 = triangle_data[1]
        density = triangle_data[2]
        restitution = triangle_data[3]
        friction = triangle_data[4]
        self._constructors['Triangle'](pos1, pos2, density, restitution,
                                       friction, share=False)

    def _construct_shared_polygon(self, data):
        polygon_data = json.loads(data)
        vertices = polygon_data[0]
        density = polygon_data[1]
        restitution = polygon_data[2]
        friction = polygon_data[3]
        self._constructors['Polygon'](vertices, density, restitution,
                                      friction, share=False)

    def _construct_shared_magicpen(self, data):
        magicpen_data = json.loads(data)
        vertices = magicpen_data[0]
        density = magicpen_data[1]
        restitution = magicpen_data[2]
        friction = magicpen_data[3]
        self._constructors['Magicpen'](vertices, density, restitution,
                                       friction, share=False)

    def _add_shared_joint(self, data):
        joint_data = json.loads(data)
        pos1 = joint_data[0]
        pos2 = joint_data[1]
        self._constructors['Joint'](pos1, pos2, share=False)

    def _add_shared_pin(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        self._constructors['Pin'](pos, share=False)

    def _add_shared_motor(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        speed = joint_data[1]
        self._constructors['Motor'](pos, speed, share=False)

    def _add_shared_track(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        color = joint_data[1]
        self._constructors['Track'](pos, color, share=False)

    def _add_shared_chain(self, data):
        joint_data = json.loads(data)
        vertices = joint_data[0]
        link_length = joint_data[1]
        radius = joint_data[2]
        self._constructors['Chain'](vertices, link_length, radius,
                                    share=False)

    def send_event(self, text):
        self._collab.post(dict(action='text', text=text))

    def _load_project(self, button):
        chooser = ObjectChooser(parent=self)
        result = chooser.run()
        if result == Gtk.ResponseType.ACCEPT:
            dsobject = chooser.get_selected_object()
            file_path = dsobject.get_file_path()
            self.__load_game(file_path, True)
            chooser.destroy()

    def __load_game(self, file_path, journal=False):
        confirmation_alert = ConfirmationAlert()
        confirmation_alert.props.title = _('Are You Sure?')
        confirmation_alert.props.msg = \
            _('All your work will be discarded. This cannot be undone!')

        def action(alert, response):
            self.remove_alert(alert)
            if response is not Gtk.ResponseType.OK:
                return

            try:
                f = open(file_path, 'r')
                # Test if the file is valid project.
                json.loads(f.read())
                f.close()

                self.read_file(file_path)
            except:
                title = _('Load project from journal')
                if not journal:
                    title = _('Load example')
                msg = _(
                    'Error: Cannot open Physics project from this file.')
                alert = NotifyAlert(5)
                alert.props.title = title
                alert.props.msg = msg
                alert.connect(
                    'response',
                    lambda alert,
                    response: self.remove_alert(alert))
                self.add_alert(alert)

        confirmation_alert.connect('response', action)
        self.add_alert(confirmation_alert)

    def _create_store(self, widget=None):
        if self._sample_window is None:
            self._sample_box = Gtk.EventBox()

            vbox = Gtk.VBox()
            self._sample_window = Gtk.ScrolledWindow()
            self._sample_window.set_policy(Gtk.PolicyType.NEVER,
                                           Gtk.PolicyType.AUTOMATIC)
            self._sample_window.set_border_width(4)

            self._sample_window.set_size_request(
                Gdk.Screen.width() / 2, Gdk.Screen.height() / 2)
            self._sample_window.show()

            store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)

            icon_view = Gtk.IconView()
            icon_view.set_model(store)
            icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE)
            icon_view.connect('selection-changed', self._sample_selected,
                              store)
            icon_view.set_pixbuf_column(0)
            icon_view.grab_focus()
            self._sample_window.add(icon_view)
            icon_view.show()
            self._fill_samples_list(store)

            title = Gtk.HBox()
            title_label = Gtk.Label(_("Select a sample..."))
            separator = Gtk.HSeparator()
            separator.props.expand = True
            separator.props.visible = False

            btn = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL)
            btn.connect('clicked', self._cancel_clicked_cb)

            title.pack_start(title_label, False, False, 5)
            title.pack_start(separator, False, False, 0)
            title.pack_end(btn, False, False, 0)

            vbox.pack_start(title, False, False, 5)
            vbox.pack_end(self._sample_window, True, True, 0)

            self._sample_box.add(vbox)
            self._sample_box.show_all()
            self._notebook.add(self._sample_box)

        self._notebook.set_current_page(1)

    def _cancel_clicked_cb(self, button=None):
        self._notebook.set_current_page(0)

    def _get_selected_path(self, widget, store):
        try:
            iter_ = store.get_iter(widget.get_selected_items()[0])
            image_path = store.get(iter_, 1)[0]

            return image_path, iter_
        except:
            return None

    def _sample_selected(self, widget, store):
        selected = self._get_selected_path(widget, store)

        if selected is None:
            self._selected_sample = None
            self._cancel_clicked_cb()
            return

        image_path, _iter = selected
        iter_ = store.get_iter(widget.get_selected_items()[0])
        image_path = store.get(iter_, 1)[0]

        self._selected_sample = image_path
        self._cancel_clicked_cb()

        self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH))
        GLib.idle_add(self._sample_loader)

    def _sample_loader(self):
        # Convert from thumbnail path to sample path
        basename = os.path.basename(self._selected_sample)[:-4]
        file_path = os.path.join(activity.get_bundle_path(),
                                 'samples', basename + '.json')
        self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR))
        self.__load_game(file_path)

    def _fill_samples_list(self, store):
        '''
        Append images from the artwork_paths to the store.
        '''
        for filepath in self._scan_for_samples():
            pixbuf = None
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filepath, 100, 100)
            store.append([pixbuf, filepath])

    def _scan_for_samples(self):
        samples = sorted(
            glob.glob(
                os.path.join(
                    activity.get_bundle_path(),
                    'samples',
                    'thumbnails',
                    '*.png')))
        return samples
Ejemplo n.º 6
0
class PhysicsActivity(activity.Activity):

    def __init__(self, handle):
        super(PhysicsActivity, self).__init__(handle)
        self.metadata['mime_type'] = 'application/x-physics-activity'
        self.add_events(Gdk.EventMask.ALL_EVENTS_MASK |
                        Gdk.EventMask.VISIBILITY_NOTIFY_MASK)

        self.connect('visibility-notify-event', self._focus_event)
        self.connect('window-state-event', self._window_event)

        self.game_canvas = sugargame.canvas.PygameCanvas(self)
        self.game = physics.main(self)

        self.preview = None
        self._sample_window = None

        self._fixed = Gtk.Fixed()
        self._fixed.put(self.game_canvas, 0, 0)

        w = Gdk.Screen.width()
        h = Gdk.Screen.height() - 2 * GRID_CELL_SIZE

        self.game_canvas.set_size_request(w, h)

        self._constructors = {}
        self.build_toolbar()

        self.set_canvas(self._fixed)
        Gdk.Screen.get_default().connect('size-changed',
                                         self.__configure_cb)

        logging.debug(os.path.join(
                      activity.get_activity_root(), 'data', 'data'))
        self.game_canvas.run_pygame(self.game.run)
        GObject.idle_add(self._setup_sharing)
        self.show_all()

    def _setup_sharing(self):
        self.we_are_sharing = False

        if self.shared_activity:
            # We're joining
            if not self.get_shared():
                xocolors = XoColor(profile.get_color().to_string())
                share_icon = Icon(icon_name='zoom-neighborhood',
                                  xo_color=xocolors)
                self._joined_alert = Alert()
                self._joined_alert.props.icon = share_icon
                self._joined_alert.props.title = _('Please wait')
                self._joined_alert.props.msg = _('Starting connection...')
                self.add_alert(self._joined_alert)
                self._waiting_cursor()

        self._setup_presence_service()

    def __configure_cb(self, event):
        ''' Screen size has changed '''
        self.write_file(os.path.join(
                        activity.get_activity_root(), 'data', 'data'))
        w = Gdk.Screen.width()
        h = Gdk.Screen.height() - 2 * GRID_CELL_SIZE
        pygame.display.set_mode((w, h),
                                pygame.RESIZABLE)
        self.read_file(os.path.join(
                       activity.get_activity_root(), 'data', 'data'))
        self.game.run(True)

    def read_file(self, file_path):
        self.game.read_file(file_path)

    def write_file(self, file_path):
        self.game.write_file(file_path)

    def get_preview(self):
        ''' Custom preview code to get image from pygame. '''
        return self.game_canvas.get_preview()

    def build_toolbar(self):
        self.max_participants = 4

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        create_toolbar = ToolbarButton()
        create_toolbar.props.page = Gtk.Toolbar()
        create_toolbar.props.icon_name = 'magicpen'
        create_toolbar.props.label = _('Create')
        toolbar_box.toolbar.insert(create_toolbar, -1)
        self._insert_create_tools(create_toolbar)

        self._insert_stop_play_button(toolbar_box.toolbar)

        clear_trace = ToolButton('clear-trace')
        clear_trace.set_tooltip(_('Clear Trace Marks'))
        clear_trace.set_accelerator(_('<ctrl>x'))
        clear_trace.connect('clicked', self.clear_trace_cb)
        clear_trace.set_sensitive(False)
        toolbar_box.toolbar.insert(clear_trace, -1)
        clear_trace.show()
        self.clear_trace = clear_trace

        self._insert_clear_all_button(toolbar_box.toolbar)

        load_example = ToolButton('load-sample')
        load_example.set_tooltip(_('Show sample projects'))
        load_example.connect('clicked', self._create_store)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        toolbar_box.toolbar.insert(load_example, -1)
        load_example.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_size_request(0, -1)
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop = StopButton(self)
        toolbar_box.toolbar.insert(stop, -1)
        stop.show()

        separator = Gtk.SeparatorToolItem()
        activity_button.props.page.insert(separator, -1)
        separator.show()

        export_json = ToolButton('save-as-json')
        export_json.set_tooltip(_('Export tracked objects to journal'))
        export_json.connect('clicked', self._export_json_cb)
        activity_button.props.page.insert(export_json, -1)
        export_json.show()

        export_csv = ToolButton('save-as-csv')
        export_csv.set_tooltip(_('Export tracked objects to journal'))
        export_csv.connect('clicked', self._export_csv_cb)
        activity_button.props.page.insert(export_csv, -1)
        export_csv.show()

        load_project = ToolButton('load-project')
        load_project.set_tooltip(_('Load project from journal'))
        load_project.connect('clicked', self._load_project)
        activity_button.props.page.insert(load_project, -1)
        load_project.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()
        create_toolbar.set_expanded(True)
        return toolbar_box

    def can_close(self):
        self.preview = self.get_preview()
        self.game.loop = False
        return True

    def _insert_stop_play_button(self, toolbar):

        self.stop_play_toolbar = ToolbarButton()
        st_toolbar = self.stop_play_toolbar
        st_toolbar.props.page = Gtk.Toolbar()
        st_toolbar.props.icon_name = 'media-playback-stop'

        self.stop_play_state = True
        self.stop_play = ToolButton('media-playback-stop')
        self.stop_play.set_tooltip(_('Stop'))
        self.stop_play.set_accelerator(_('<ctrl>space'))
        self.stop_play.connect('clicked', self.stop_play_cb)
        self._insert_item(st_toolbar, self.stop_play)
        self.stop_play.show()

        slowest_button = RadioToolButton(group=None)
        slowest_button.set_icon_name('slow-walk-milton-raposo')
        slowest_button.set_tooltip(_('Run slower'))
        slowest_button.connect('clicked', self._set_fps_cb, SLOWEST_FPS)
        self._insert_item(st_toolbar, slowest_button)
        slowest_button.show()

        slow_button = RadioToolButton(group=slowest_button)
        slow_button.set_icon_name('walking')
        slow_button.set_tooltip(_('Run slow'))
        slow_button.connect('clicked', self._set_fps_cb, SLOW_FPS)
        self._insert_item(st_toolbar, slow_button)
        slow_button.show()

        fast_button = RadioToolButton(group=slowest_button)
        fast_button.set_icon_name('running')
        fast_button.set_tooltip('Run fast')
        fast_button.connect('clicked', self._set_fps_cb, FAST_FPS)
        self._insert_item(st_toolbar, fast_button)
        fast_button.show()
        fast_button.set_active(True)

        toolbar.insert(self.stop_play_toolbar, -1)
        self.stop_play_toolbar.show_all()

    def _set_fps_cb(self, button, value):
        self.game.set_game_fps(value)

    def _insert_clear_all_button(self, toolbar):
        self.clear_all = ToolButton('clear_all')
        self.clear_all.set_tooltip(_('Erase All'))
        self.clear_all.set_accelerator(_('<ctrl>a'))
        self.clear_all.connect('clicked', self.clear_all_cb)
        toolbar.insert(self.clear_all, -1)
        self.clear_all.set_sensitive(False)
        self.clear_all.show()

    def _insert_item(self, toolbar, item, pos=-1):
        if hasattr(toolbar, 'insert'):
            toolbar.insert(item, pos)
        else:
            toolbar.props.page.insert(item, pos)

    def _insert_create_tools(self, create_toolbar):
        # Make + add the component buttons
        self.radioList = {}
        for i, c in enumerate(tools.allTools):
            if i == 0:
                button = RadioToolButton(group=None)
                firstbutton = button
            else:
                button = RadioToolButton(group=firstbutton)
            button.set_icon_name(c.icon)
            button.set_tooltip(c.toolTip)
            button.set_accelerator(c.toolAccelerator)
            button.connect('clicked', self.radioClicked)
            palette = self._build_palette(c)
            if palette is not None:
                palette.show()
                button.get_palette().set_content(palette)
            self._insert_item(create_toolbar, button, -1)
            button.show()
            self.radioList[button] = c.name
            if hasattr(c, 'constructor'):
                self._constructors[c.name] = \
                    self.game.toolList[c.name].constructor

    def __icon_path(self, name):
        activity_path = activity.get_bundle_path()
        icon_path = os.path.join(activity_path, 'icons',
                                 name + '.svg')
        return icon_path

    def _build_palette(self, tool):
        if tool.palette_enabled:
            if tool.palette_mode == tools.PALETTE_MODE_ICONS:
                grid = Gtk.Grid()
                for s, settings in enumerate(tool.palette_settings):
                    self.game.toolList[tool.name].buttons.append([])
                    for i, icon_value in enumerate(settings['icon_values']):
                        if i == 0:
                            button = RadioToolButton(group=None)
                            firstbutton = button
                        else:
                            button = RadioToolButton(group=firstbutton)
                        button.set_icon_name(settings['icons'][i])
                        button.connect('clicked',
                                       self._palette_icon_clicked,
                                       tool.name,
                                       s,
                                       settings['name'],
                                       icon_value)
                        grid.attach(button, i, s, 1, 1)
                        self.game.toolList[tool.name].buttons[s].append(button)
                        button.show()
                        if settings['active'] == settings['icons'][i]:
                            button.set_icon_name(settings['icons'][i] +
                                                 '-selected')
                            button.set_active(True)
                return grid
        else:
            return None

    def _palette_icon_clicked(self, widget, toolname, s, value_name, value):
        for tool in tools.allTools:
            if tool.name == toolname:
                # Radio buttons are not highlighting in the palette
                # so adding highlight by hand
                # See http://bugs.sugarlabs.org/ticket/305
                setting = self.game.toolList[tool.name].palette_settings[s]
                for i, button in enumerate(
                        self.game.toolList[tool.name].buttons[s]):
                    icon_name = setting['icons'][i]
                    if button == widget:
                        button.set_icon_name(icon_name + '-selected')
                    else:
                        button.set_icon_name(icon_name)
                if hasattr(tool, 'palette_data_type'):
                    tool.palette_data_type = value
                else:
                    tool.palette_data[value_name] = value

    def clear_trace_alert_cb(self, alert, response):
        self.remove_alert(alert)
        if response is Gtk.ResponseType.OK:
            self.game.full_pos_list = [[] for _ in self.game.full_pos_list]
            self.game.tracked_bodies = 0

    def clear_trace_cb(self, button):
        clear_trace_alert = ConfirmationAlert()
        clear_trace_alert.props.title = _('Are You Sure?')
        clear_trace_alert.props.msg = \
            _('All trace points will be erased. This cannot be undone!')
        clear_trace_alert.connect('response', self.clear_trace_alert_cb)
        self.add_alert(clear_trace_alert)

    def stop_play_cb(self, button):
        pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                             action='stop_start_toggle'))
        self.stop_play_state = not self.stop_play_state

        if self.stop_play_state:
            self.stop_play.set_icon_name('media-playback-stop')
            self.stop_play.set_tooltip(_('Stop'))

            self.stop_play_toolbar.set_icon_name('media-playback-stop')
        else:
            self.stop_play.set_icon_name('media-playback-start')
            self.stop_play.set_tooltip(_('Start'))

            self.stop_play_toolbar.set_icon_name('media-playback-start')

    def clear_all_cb(self, button):
        def clear_all_alert_cb(alert, response_id):
            self.remove_alert(alert)
            if response_id is Gtk.ResponseType.OK:
                pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                     action='clear_all'))
        if len(self.game.world.world.GetBodyList()) > 2:
            clear_all_alert = ConfirmationAlert()
            clear_all_alert.props.title = _('Are You Sure?')
            clear_all_alert.props.msg = \
                _('All your work will be discarded. This cannot be undone!')
            clear_all_alert.connect('response', clear_all_alert_cb)
            self.add_alert(clear_all_alert)

    def radioClicked(self, button):
        pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                             action=self.radioList[button]))

    def _focus_event(self, event, data=None):
        ''' Send focus events to pygame to allow it to idle when in
        background. '''
        if not self.game.pygame_started:
            logging.debug('focus_event: pygame not yet initialized')
            return
        if data.state == Gdk.VisibilityState.FULLY_OBSCURED:
            pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                 action='focus_out'))
        else:
            self.game.show_fake_cursor = True
            pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                 action='focus_in'))

    def _export_json_cb(self, button):
        jobject = datastore.create()
        jobject.metadata['title'] = _('Physics export')
        jobject.metadata['mime_type'] = 'text/plain'

        tmp_dir = os.path.join(self.get_activity_root(), 'instance')
        fd, file_path = tempfile.mkstemp(dir=tmp_dir)
        os.close(fd)

        data = self.game.full_pos_list
        jsonfile = open(file_path, 'wb')
        jsonfile.write(json.dumps(data))
        jsonfile.close()

        jobject.set_file_path(os.path.abspath(jsonfile.name))
        datastore.write(jobject)

    def _export_csv_cb(self, button):
        jobject = datastore.create()
        jobject.metadata['title'] = _('Physics export')
        jobject.metadata['mime_type'] = 'text/csv'

        tmp_dir = os.path.join(self.get_activity_root(), 'instance')
        fd, file_path = tempfile.mkstemp(dir=tmp_dir)
        os.close(fd)

        data = self.game.full_pos_list
        csvfile = open(file_path, 'wb')
        writer = csv.writer(csvfile)
        writer.writerows(data)
        csvfile.close()

        jobject.set_file_path(os.path.abspath(csvfile.name))
        datastore.write(jobject)

    def _window_event(self, window, event):
        ''' Send focus out event to pygame when switching to a desktop
        view. '''
        if event.changed_mask & Gdk.WindowState.ICONIFIED:
            pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                 action='focus_out'))

    def _setup_presence_service(self):
        ''' Setup the Presence Service. '''
        self.pservice = presenceservice.get_instance()

        owner = self.pservice.get_owner()
        self.owner = owner
        self.buddies = [owner]
        self._share = ''
        self.connect('shared', self._shared_cb)
        self.connect('joined', self._joined_cb)

    def _shared_cb(self, activity):
        ''' Either set up initial share...'''
        if self.get_shared_activity() is None:
            logging.error('Failed to share or join activity ... \
                shared_activity is null in _shared_cb()')
            return

        self.initiating = True
        self.waiting = False
        self.we_are_sharing = True
        logging.debug('I am sharing...')

        self.conn = self.shared_activity.telepathy_conn
        self.tubes_chan = self.shared_activity.telepathy_tubes_chan
        self.text_chan = self.shared_activity.telepathy_text_chan

        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
            'NewTube', self._new_tube_cb)

        logging.debug('This is my activity: making a tube...')
        id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
            SERVICE, {})

    def _joined_cb(self, activity):
        ''' ...or join an exisiting share. '''
        if self.get_shared_activity() is None:
            logging.error('Failed to share or join activity ... \
                shared_activity is null in _shared_cb()')
            return

        self.initiating = False
        logging.debug('I joined a shared activity.')

        self.conn = self.shared_activity.telepathy_conn
        self.tubes_chan = self.shared_activity.telepathy_tubes_chan
        self.text_chan = self.shared_activity.telepathy_text_chan

        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
            'NewTube', self._new_tube_cb)

        logging.debug('I am joining an activity: waiting for a tube...')
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
            reply_handler=self._list_tubes_reply_cb,
            error_handler=self._list_tubes_error_cb)

        self.waiting = True

        if self._joined_alert is not None:
            self.remove_alert(self._joined_alert)
            self._joined_alert = None
            self._restore_cursor()
            self.we_are_sharing = True

    def _restore_cursor(self):
        ''' No longer waiting, so restore standard cursor. '''
        if not hasattr(self, 'get_window'):
            return
        self.get_window().set_cursor(self.old_cursor)

    def _waiting_cursor(self):
        ''' Waiting, so set watch cursor. '''
        if not hasattr(self, 'get_window'):
            return
        self.old_cursor = self.get_window().get_cursor()
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))

    def _list_tubes_reply_cb(self, tubes):
        ''' Reply to a list request. '''
        for tube_info in tubes:
            self._new_tube_cb(*tube_info)

    def _list_tubes_error_cb(self, e):
        ''' Log errors. '''
        logging.error('ListTubes() failed: %s', e)

    def _new_tube_cb(self, id, initiator, type, service, params, state):
        ''' Create a new tube. '''
        logging.debug('New tube: ID=%d initator=%d type=%d service=%s '
                      'params=%r state=%d', id, initiator, type, service,
                      params, state)

        if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE):
            if state == telepathy.TUBE_STATE_LOCAL_PENDING:
                self.tubes_chan[
                    telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)

            tube_conn = TubeConnection(
                self.conn,
                self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id,
                group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP])

            self.chattube = ChatTube(tube_conn, self.initiating,
                                     self.event_received_cb)

    def event_received_cb(self, text):
        ''' Data is passed as tuples: cmd:text '''
        dispatch_table = {'C': self._construct_shared_circle,
                          'B': self._construct_shared_box,
                          'T': self._construct_shared_triangle,
                          'P': self._construct_shared_polygon,
                          'M': self._construct_shared_magicpen,
                          'j': self._add_shared_joint,
                          'p': self._add_shared_pin,
                          'm': self._add_shared_motor,
                          't': self._add_shared_track,
                          'c': self._add_shared_chain,
                          }
        logging.debug('<<< %s' % (text[0]))
        dispatch_table[text[0]](text[2:])

    def _construct_shared_circle(self, data):
        circle_data = json.loads(data)
        pos = circle_data[0]
        radius = circle_data[1]
        density = circle_data[2]
        restitution = circle_data[3]
        friction = circle_data[4]
        self._constructors['Circle'](pos, radius, density, restitution,
                                     friction, share=False)

    def _construct_shared_box(self, data):
        box_data = json.loads(data)
        pos1 = box_data[0]
        pos2 = box_data[1]
        density = box_data[2]
        restitution = box_data[3]
        friction = box_data[4]
        self._constructors['Box'](pos1, pos2, density, restitution,
                                  friction, share=False)

    def _construct_shared_triangle(self, data):
        triangle_data = json.loads(data)
        pos1 = triangle_data[0]
        pos2 = triangle_data[1]
        density = triangle_data[2]
        restitution = triangle_data[3]
        friction = triangle_data[4]
        self._constructors['Triangle'](pos1, pos2, density, restitution,
                                       friction, share=False)

    def _construct_shared_polygon(self, data):
        polygon_data = json.loads(data)
        vertices = polygon_data[0]
        density = polygon_data[1]
        restitution = polygon_data[2]
        friction = polygon_data[3]
        self._constructors['Polygon'](vertices, density, restitution,
                                      friction, share=False)

    def _construct_shared_magicpen(self, data):
        magicpen_data = json.loads(data)
        vertices = magicpen_data[0]
        density = magicpen_data[1]
        restitution = magicpen_data[2]
        friction = magicpen_data[3]
        self._constructors['Magicpen'](vertices, density, restitution,
                                       friction, share=False)

    def _add_shared_joint(self, data):
        joint_data = json.loads(data)
        pos1 = joint_data[0]
        pos2 = joint_data[1]
        self._constructors['Joint'](pos1, pos2, share=False)

    def _add_shared_pin(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        self._constructors['Pin'](pos, share=False)

    def _add_shared_motor(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        speed = joint_data[1]
        self._constructors['Motor'](pos, speed, share=False)

    def _add_shared_track(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        color = joint_data[1]
        self._constructors['Track'](pos, color, share=False)

    def _add_shared_chain(self, data):
        joint_data = json.loads(data)
        vertices = joint_data[0]
        link_length = joint_data[1]
        radius = joint_data[2]
        self._constructors['Chain'](vertices, link_length, radius,
                                    share=False)

    def send_event(self, text):
        ''' Send event through the tube. '''
        if hasattr(self, 'chattube') and self.chattube is not None:
            logging.debug('>>> %s' % (text[0]))
            self.chattube.SendText(text)

    def _load_project(self, button):
        chooser = ObjectChooser(parent=self)
        result = chooser.run()
        if result == Gtk.ResponseType.ACCEPT:
            dsobject = chooser.get_selected_object()
            file_path = dsobject.get_file_path()
            self.__load_game(file_path, True)
            chooser.destroy()

    def __load_game(self, file_path, journal=False):
        confirmation_alert = ConfirmationAlert()
        confirmation_alert.props.title = _('Are You Sure?')
        confirmation_alert.props.msg = \
            _('All your work will be discarded. This cannot be undone!')

        def action(alert, response):
            self.remove_alert(alert)
            if not response is Gtk.ResponseType.OK:
                return

            try:
                f = open(file_path, 'r')
                # Test if the file is valid project.
                json.loads(f.read())
                f.close()

                self.read_file(file_path)
                self.game.run(True)
            except:
                title = _('Load project from journal')
                if not journal:
                    title = _('Load example')
                msg = _(
                    'Error: Cannot open Physics project from this file.')
                alert = NotifyAlert(5)
                alert.props.title = title
                alert.props.msg = msg
                alert.connect(
                    'response',
                    lambda alert,
                    response: self.remove_alert(alert))
                self.add_alert(alert)

        confirmation_alert.connect('response', action)
        self.add_alert(confirmation_alert)

    def _create_store(self, widget=None):
        width = Gdk.Screen.width() / 4
        height = Gdk.Screen.height() / 4

        if self._sample_window is None:
            self._sample_box = Gtk.EventBox()

            vbox = Gtk.VBox()
            self._sample_window = Gtk.ScrolledWindow()
            self._sample_window.set_policy(Gtk.PolicyType.NEVER,
                                           Gtk.PolicyType.AUTOMATIC)
            self._sample_window.set_border_width(4)
            w = Gdk.Screen.width() / 2
            h = Gdk.Screen.height() / 2
            self._sample_window.set_size_request(w, h)
            self._sample_window.show()

            store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)

            icon_view = Gtk.IconView()
            icon_view.set_model(store)
            icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE)
            icon_view.connect('selection-changed', self._sample_selected,
                              store)
            icon_view.set_pixbuf_column(0)
            icon_view.grab_focus()
            self._sample_window.add(icon_view)
            icon_view.show()
            self._fill_samples_list(store)

            title = Gtk.HBox()
            title_label = Gtk.Label(_("Select a sample..."))
            separator = Gtk.HSeparator()
            separator.props.expand = True
            separator.props.visible = False

            btn = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL)
            btn.connect('clicked', lambda button: self._sample_box.hide())

            title.pack_start(title_label, False, False, 5)
            title.pack_start(separator, False, False, 0)
            title.pack_end(btn, False, False, 0)

            vbox.pack_start(title, False, False, 5)
            vbox.pack_end(self._sample_window, True, True, 0)

            self._sample_box.add(vbox)

            self._fixed.put(self._sample_box, width, height)

        if self._sample_window:
            # Remove and add again. Maybe its on portrait mode.
            self._fixed.remove(self._sample_box)
            self._fixed.put(self._sample_box, width, height)

        self._sample_box.show_all()

    def _get_selected_path(self, widget, store):
        try:
            iter_ = store.get_iter(widget.get_selected_items()[0])
            image_path = store.get(iter_, 1)[0]

            return image_path, iter_
        except:
            return None

    def _sample_selected(self, widget, store):
        selected = self._get_selected_path(widget, store)

        if selected is None:
            self._selected_sample = None
            self._sample_box.hide()
            return

        image_path, _iter = selected
        iter_ = store.get_iter(widget.get_selected_items()[0])
        image_path = store.get(iter_, 1)[0]

        self._selected_sample = image_path
        self._sample_box.hide()

        self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH))
        GObject.idle_add(self._sample_loader)

    def _sample_loader(self):
        # Convert from thumbnail path to sample path
        basename = os.path.basename(self._selected_sample)[:-4]
        file_path = os.path.join(activity.get_bundle_path(),
                                 'samples', basename + '.json')
        self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR))
        self.__load_game(file_path)

    def _fill_samples_list(self, store):
        '''
        Append images from the artwork_paths to the store.
        '''
        for filepath in self._scan_for_samples():
            pixbuf = None
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filepath, 100, 100)
            store.append([pixbuf, filepath])

    def _scan_for_samples(self):
        samples = sorted(
            glob.glob(
                os.path.join(
                    activity.get_bundle_path(),
                    'samples',
                    'thumbnails',
                    '*.png')))
        return samples
Ejemplo n.º 7
0
class TuningToolbar(Gtk.Toolbar):
    ''' The toolbar for tuning instruments '''
    def __init__(self, activity):
        super(type(self), self).__init__()

        self.activity = activity
        self._show_tuning_line = False
        self._updating_note = True
        self._tuning_tool = None

        self._instrument_button = ToolButton('instruments')
        self._instrument_button.set_tooltip(_('Tune an instrument.'))
        self._instrument_button.connect('clicked', self._button_selection_cb)
        self.insert(self._instrument_button, -1)
        self._setup_instrument_palette()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = True
        self.insert(separator, -1)

        self._note = 'A'
        self._notes_button = ToolButton('notes')
        self._notes_button.set_tooltip(_('Notes'))
        self._notes_button.connect('clicked', self._button_selection_cb)
        self.insert(self._notes_button, -1)
        self._setup_notes_palette()

        self._octave = 4
        self._octaves_button = ToolButton('octaves')
        self._octaves_button.set_tooltip(_('Octaves'))
        self._octaves_button.connect('clicked', self._button_selection_cb)
        self.insert(self._octaves_button, -1)
        self._setup_octaves_palette()

        # The entry is used to display a note or for direct user input
        self._freq_entry = Gtk.Entry()
        self._freq_entry.set_text('440')  # A
        self._freq_entry_changed_id = self._freq_entry.connect(
            'changed', self._update_freq_entry)
        if hasattr(self._freq_entry, 'set_tooltip_text'):
            self._freq_entry.set_tooltip_text(
                _('Enter a frequency to display.'))
        self._freq_entry.set_width_chars(8)
        self._freq_entry.show()
        toolitem = Gtk.ToolItem()
        toolitem.add(self._freq_entry)
        self.insert(toolitem, -1)
        toolitem.show()

        self._new_tuning_line = ToolButton('tuning-tools')
        self._new_tuning_line.show()
        self.insert(self._new_tuning_line, -1)
        self._new_tuning_line.set_tooltip(_('Show tuning line.'))
        self._new_tuning_line.connect('clicked', self.tuning_line_cb)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = True
        self.insert(separator, -1)

        self._harmonic = ToolButton('harmonics')
        self._harmonic.show()
        self.insert(self._harmonic, -1)
        self._harmonic.set_tooltip(_('Show harmonics.'))
        self._harmonic.connect('clicked', self.harmonic_cb)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = True
        self.insert(separator, -1)

        self._play_tone = ToolButton('media-playback-start')
        self._play_tone.show()
        self.insert(self._play_tone, -1)
        self._play_tone.set_tooltip(_('Play a note.'))
        self._play_tone.connect('clicked', self.play_cb)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self.insert(separator, -1)

        self.label = Gtk.Label(label='')
        self.label.set_use_markup(True)
        self.label.show()
        toolitem = Gtk.ToolItem()
        toolitem.add(self.label)
        self.insert(toolitem, -1)
        toolitem.show()

        self.show_all()

    def _update_note(self):
        ''' Calculate the frequency based on note and octave '''
        if not hasattr(self, '_freq_entry'):  # Still setting up toolbar
            return
        i = self._octave * 12 + NOTES.index(self._note)
        freq = A0 * pow(TWELTHROOT2, i)
        self._updating_note = True
        self._freq_entry.set_text('%0.3f' % (freq))
        self.label.set_markup(
            SPAN %
            (style.COLOR_WHITE.get_html(), self._note + str(self._octave)))
        if self._show_tuning_line:
            self.activity.wave.tuning_line = freq
        return

    def _update_freq_entry(self, widget):
        # Calculate a note from a frequency
        if not self._updating_note:  # Only if user types in a freq.
            try:
                freq = float(self._freq_entry.get_text())
                # Only consider notes in piano range
                if freq < A0 * 0.97:
                    self.label.set_text('< A0')
                    return
                if freq > C8 * 1.03:
                    self.label.set_text('> C8')
                    return
                self.label.set_markup(freq_note(freq, flatsharp=True))
            except ValueError:
                return

        self._updating_note = False

    def _button_selection_cb(self, widget):
        palette = widget.get_palette()
        if palette:
            if not palette.is_up():
                palette.popup(immediate=True)
            else:
                palette.popdown(immediate=True)
            return

    def _setup_notes_palette(self):
        self._notes_palette = self._notes_button.get_palette()

        for note in NOTES:
            menu_item = MenuItem(icon_name='', text_label=note)
            menu_item.connect('activate', self._note_selected_cb, note)
            self._notes_palette.menu.append(menu_item)
            menu_item.show()

    def _note_selected_cb(self, widget, note):
        self._note = note
        self._update_note()

    def _setup_octaves_palette(self):
        self._octaves_palette = self._octaves_button.get_palette()

        for octave in range(9):
            menu_item = MenuItem(icon_name='', text_label=str(octave))
            menu_item.connect('activate', self._octave_selected_cb, octave)
            self._octaves_palette.menu.append(menu_item)
            menu_item.show()

    def _octave_selected_cb(self, widget, octave):
        self._octave = octave
        self._update_note()

    def _setup_instrument_palette(self):
        self.instrument_palette = self._instrument_button.get_palette()

        self.instrument = []
        for k in INSTRUMENT_DICT.keys():
            self.instrument.append(k)
            menu_item = MenuItem(icon_name='', text_label=k)
            menu_item.connect('activate', self.instrument_selected_cb, k)
            self.instrument_palette.menu.append(menu_item)
            menu_item.show()

    def instrument_selected_cb(self, button, instrument):
        ''' Callback for instrument control '''
        logging.debug(instrument)
        if self._tuning_tool is not None:
            self.remove(self._tuning_tool)

        # Always remove previous buttons
        if hasattr(self, '_tuning_button'):
            self.remove(self._tuning_button)
        self.remove(self._notes_button)
        self.remove(self._octaves_button)

        if instrument == _('None'):
            self.activity.wave.instrument = None

            # Restore the notes, octaves buttons
            if hasattr(self, '_notes_button'):
                self.insert(self._notes_button, 2)
                self.insert(self._octaves_button, 3)
            return

        self.activity.wave.instrument = instrument

        # If we are not already in freq. base, switch.
        if not self.activity.wave.get_fft_mode():
            self.activity.timefreq_control()

        # Add a Tuning palette for this instrument
        self._tuning_button = ToolButton('notes')
        self._tuning_button.set_tooltip(instrument)
        self._tuning_button.connect('clicked', self._button_selection_cb)
        self.insert(self._tuning_button, 2)
        self._setup_tuning_palette(instrument)

    def _setup_tuning_palette(self, instrument):
        self._tuning_palette = self._tuning_button.get_palette()

        self.tuning = []
        self.tuning.append(_('All notes'))
        menu_item = MenuItem(icon_name='', text_label=_('All notes'))
        menu_item.connect('activate', self._tuning_selected_cb, instrument, -1)
        self._tuning_palette.menu.append(menu_item)
        menu_item.show()

        for i, f in enumerate(INSTRUMENT_DICT[instrument]):
            self.tuning.append(freq_note(f))
            menu_item = MenuItem(icon_name='', text_label=freq_note(f))
            menu_item.connect('activate', self._tuning_selected_cb, instrument,
                              i)
            self._tuning_palette.menu.append(menu_item)
            menu_item.show()

        self.show_all()

    def _tuning_selected_cb(self, widget, instrument, fidx):
        ''' Update note '''
        if not hasattr(self, '_freq_entry'):  # Still setting up toolbar?
            return

        if instrument not in INSTRUMENT_DICT:
            return

        if fidx == -1:  # All notes
            self.activity.wave.instrument = instrument
            self.activity.wave.tuning_line = 0.0
            self._new_tuning_line.set_icon_name('tuning-tools')
            self._new_tuning_line.set_tooltip(_('Show tuning line.'))
            self._show_tuning_line = False
        else:
            freq = INSTRUMENT_DICT[instrument][fidx]
            self.activity.wave.instrument = None
            self.activity.wave.tuning_line = freq
            self._new_tuning_line.set_icon_name('tuning-tools-off')
            self._new_tuning_line.set_tooltip(_('Hide tuning line.'))
            self._show_tuning_line = True

        # For consistency, update the freq_entry to make it audible too
        self._freq_entry.set_text(str(self.activity.wave.tuning_line))

        self._updating_note = False

    def harmonic_cb(self, *args):
        ''' Callback for harmonics control '''
        self.activity.wave.harmonics = not self.activity.wave.harmonics
        if self.activity.wave.harmonics:
            self._harmonic.set_icon_name('harmonics-off')
            self._harmonic.set_tooltip(_('Hide harmonics.'))
            if self.activity.wave.instrument is None and \
               self.activity.wave.tuning_line == 0.0:
                self._load_tuning_line()
        else:
            self._harmonic.set_icon_name('harmonics')
            self._harmonic.set_tooltip(_('Show harmonics.'))

    def tuning_line_cb(self, *args):
        ''' Callback for tuning insert '''
        if self._show_tuning_line:
            self.activity.wave.tuning_line = 0.0
            self._new_tuning_line.set_icon_name('tuning-tools')
            self._new_tuning_line.set_tooltip(_('Show tuning line.'))
            self._show_tuning_line = False
        else:
            self._load_tuning_line()

    def _load_tuning_line(self):
        ''' Read the freq entry and use value to set tuning line '''
        freq = self._freq_entry.get_text()
        try:
            self.activity.wave.tuning_line = float(freq)
            if freq < 0:
                freq = -freq
            self._new_tuning_line.set_icon_name('tuning-tools-off')
            self._new_tuning_line.set_tooltip(_('Hide tuning line.'))
            self._show_tuning_line = True
        except ValueError:
            self.activity.wave.tuning_line = 0.0
            self._freq_entry.set_text('0')
        # If we are not already in freq. base, switch.
        if not self.activity.wave.get_fft_mode():
            self.activity.timefreq_control()

    def play_cb(self, *args):
        ''' Save settings, turn off display, and then play a tone at
        the current frequency '''
        channels = []
        for c in range(self.activity.audiograb.channels):
            channels.append(self.activity.wave.get_visibility(channel=c))
            self.activity.wave.set_visibility(False, channel=c)
        wave_status = self.activity.wave.get_active()
        self.activity.wave.set_context_off()
        self.activity.wave.set_active(False)
        if self.activity.hw in [XO4, XO175]:
            self.activity.audiograb.stop_grabbing()

        freq = float(self._freq_entry.get_text())
        GLib.timeout_add(200, self.play_sound, freq, channels, wave_status)

    def play_sound(self, freq, channels, wave_status):
        ''' Play the sound and then restore wave settings '''
        self._play_sinewave(freq, 5000, 1)

        if self.activity.hw in [XO4, XO175]:
            self.activity.sensor_toolbar.set_mode('sound')
            self.activity.sensor_toolbar.set_sound_context()
            self.activity.audiograb.start_grabbing()
        for c in range(self.activity.audiograb.channels):
            self.activity.wave.set_visibility(channels[c], channel=c)
        self.activity.wave.set_context_on()
        self.activity.wave.set_active(wave_status)

    def _play_sinewave(self, pitch, amplitude=5000, duration=1):
        """ Create a Csound score to play a sine wave. """
        self.orchlines = []
        self.scorelines = []
        self.instrlist = []

        try:
            pitch = abs(float(pitch))
            amplitude = abs(float(amplitude))
            duration = abs(float(duration))
        except ValueError:
            logging.error('bad args to _play_sinewave')
            return

        self._prepare_sinewave(pitch, amplitude, duration)

        path = os.path.join(self.activity.get_activity_root(), 'instance',
                            'tmp.csd')
        # Create a csound file from the score.
        self._audio_write(path)

        # Play the csound file.
        check_output(['csound', path], 'call to csound failed?')

    def _prepare_sinewave(self,
                          pitch,
                          amplitude,
                          duration,
                          starttime=0,
                          pitch_envelope=99,
                          amplitude_envelope=100,
                          instrument=1):

        pitenv = pitch_envelope
        ampenv = amplitude_envelope
        if 1 not in self.instrlist:
            self.orchlines.append("instr 1\n")
            self.orchlines.append("kpitenv oscil 1, 1/p3, p6\n")
            self.orchlines.append("aenv oscil 1, 1/p3, p7\n")
            self.orchlines.append("asig oscil p5*aenv, p4*kpitenv, p8\n")
            self.orchlines.append("out asig\n")
            self.orchlines.append("endin\n\n")
            self.instrlist.append(1)

        self.scorelines.append(
            "i1 %s %s %s %s %s %s %s\n" %
            (str(starttime), str(duration), str(pitch), str(amplitude),
             str(pitenv), str(ampenv), str(instrument)))

    def _audio_write(self, file):
        """ Compile a .csd file. """

        csd = open(file, "w")
        csd.write("<CsoundSynthesizer>\n\n")
        csd.write("<CsOptions>\n")
        csd.write("-+rtaudio=alsa -odevaudio -m0 -d -b256 -B512\n")
        csd.write("</CsOptions>\n\n")
        csd.write("<CsInstruments>\n\n")
        csd.write("sr=16000\n")
        csd.write("ksmps=50\n")
        csd.write("nchnls=1\n\n")
        for line in self.orchlines:
            csd.write(line)
        csd.write("\n</CsInstruments>\n\n")
        csd.write("<CsScore>\n\n")
        csd.write("f1 0 2048 10 1\n")
        csd.write("f2 0 2048 10 1 0 .33 0 .2 0 .143 0 .111\n")
        csd.write("f3 0 2048 10 1 .5 .33 .25 .2 .175 .143 .125 .111 .1\n")
        csd.write("f10 0 2048 10 1 0 0 .3 0 .2 0 0 .1\n")
        csd.write("f99 0 2048 7 1 2048 1\n")
        csd.write("f100 0 2048 7 0. 10 1. 1900 1. 132 0.\n")
        csd.write(self.scorelines.pop())
        csd.write("e\n")
        csd.write("\n</CsScore>\n")
        csd.write("\n</CsoundSynthesizer>")
        csd.close()
Ejemplo n.º 8
0
class RecordControl():

    def __init__(self, toolbar):

        self._timer_value = TIMER_VALUES[0]
        self._timer_button = ToolButton('timer-0')
        self._timer_button.set_tooltip(_('Select timer'))
        self._timer_button.connect('clicked', self._timer_selection_cb)
        toolbar.insert(self._timer_button, -1)
        self._setup_timer_palette()

        self._duration_value = DURATION_VALUES[0]
        self._duration_button = ToolButton('duration-2')
        self._duration_button.set_tooltip(_('Select duration'))
        self._duration_button.connect('clicked', self._duration_selection_cb)
        toolbar.insert(self._duration_button, -1)
        self._setup_duration_palette()

        self._quality_value = 0
        self._quality_button = ToolButton('low-quality')
        self._quality_button.set_tooltip(_('Select quality'))
        self._quality_button.connect('clicked', self._quality_selection_cb)
        toolbar.insert(self._quality_button, -1)
        self._setup_quality_palette()

    def _timer_selection_cb(self, widget):
        if self._timer_palette:

            if not self._timer_palette.is_up():
                self._timer_palette.popup(immediate=True)
            else:
                self._timer_palette.popdown(immediate=True)
            return

    def _setup_timer_palette(self):
        self._timer_palette = self._timer_button.get_palette()
        box = PaletteMenuBox()
        self._timer_palette.set_content(box)
        box.show()

        for seconds in TIMER_VALUES:
            if seconds == 0:
                text = _('Immediate')
            else:
                text = ngettext('%s second', '%s seconds', seconds) % seconds
            menu_item = PaletteMenuItem(text, icon_name='timer-%d' % (seconds))
            menu_item.connect('activate', self._timer_selected_cb, seconds)
            box.append_item(menu_item)
            menu_item.show()

    def _timer_selected_cb(self, button, seconds):
        self.set_timer_idx(TIMER_VALUES.index(seconds))

    def _duration_selection_cb(self, widget):
        if self._duration_palette:
            if not self._duration_palette.is_up():
                self._duration_palette.popup(immediate=True)
            else:
                self._duration_palette.popdown(immediate=True)
            return

    def _setup_duration_palette(self):
        self._duration_palette = self._duration_button.get_palette()
        box = PaletteMenuBox()
        self._duration_palette.set_content(box)
        box.show()

        for minutes in DURATION_VALUES:
            if minutes == 0:
                text = Gtk.Label(_('Immediate'))
            else:
                text = ngettext('%s minute', '%s minutes', minutes) % minutes
            menu_item = PaletteMenuItem(text,
                                        icon_name='duration-%d' % (minutes))
            menu_item.connect('activate', self._duration_selected_cb, minutes)
            box.append_item(menu_item)
            menu_item.show()

    def _duration_selected_cb(self, button, minutes):
        self.set_duration_idx(DURATION_VALUES.index(minutes))

    def _quality_selection_cb(self, widget):
        if self._quality_palette:
            if not self._quality_palette.is_up():
                self._quality_palette.popup(immediate=True)
            else:
                self._quality_palette.popdown(immediate=True)
            return

    def _setup_quality_palette(self):
        self._quality_palette = self._quality_button.get_palette()
        box = PaletteMenuBox()
        self._quality_palette.set_content(box)
        box.show()

        for quality in QUALITY_VALUES:
            text = _('%s quality') % (quality)
            menu_item = PaletteMenuItem(text, icon_name=quality + '-quality')
            menu_item.connect('activate', self._quality_selected_cb, quality)
            box.append_item(menu_item)
            menu_item.show()

    def _quality_selected_cb(self, button, quality):
        self.set_quality(QUALITY_VALUES.index(quality))

    def set_mode(self, mode):
        if mode == constants.MODE_PHOTO:
            self._quality_button.set_sensitive(False)
            self._timer_button.set_sensitive(True)
            self._duration_button.set_sensitive(False)
        if mode == constants.MODE_VIDEO:
            self._quality_button.set_sensitive(True)
            self._timer_button.set_sensitive(True)
            self._duration_button.set_sensitive(True)
        if mode == constants.MODE_AUDIO:
            self._quality_button.set_sensitive(False)
            self._timer_button.set_sensitive(True)
            self._duration_button.set_sensitive(True)

    def get_timer(self):
        return self._timer_value

    def get_timer_idx(self):
        if self._timer_value in TIMER_VALUES:
            return TIMER_VALUES.index(self._timer_value)
        else:
            return TIMER_VALUES[0]

    def set_timer_idx(self, idx):
        self._timer_value = TIMER_VALUES[idx]
        if hasattr(self, '_timer_button'):
            self._timer_button.set_icon_name('timer-%d' % (self._timer_value))

    def get_duration(self):
        return self._duration_value

    def get_duration_idx(self):
        if self._duration_value in DURATION_VALUES:
            return DURATION_VALUES.index(self._duration_value)
        else:
            return DURATION_VALUES[0]

    def set_duration_idx(self, idx):
        self._duration_value = DURATION_VALUES[idx]
        if hasattr(self, '_duration_button'):
            self._duration_button.set_icon_name(
                'duration-%d' % (self._duration_value))

    def get_quality(self):
        return self._quality_value

    def set_quality(self, idx):
        self._quality_value = idx
        if hasattr(self, '_quality_button'):
            name = '%s-quality' % (QUALITY_VALUES[idx])
            self._quality_button.set_icon_name(name)
Ejemplo n.º 9
0
class ErikosActivity(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self.max_participants = 1

        toolbar_box = ToolbarBox()

        # Buttons added to the Activity toolbar
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        # Play Button
        self.play = ToolButton("media-playback-start")
        self.play.set_tooltip(_('Play'))
        self.play.props.sensitive = True
        self.play.connect('clicked', self._play_cb)
        toolbar_box.toolbar.insert(self.play, -1)
        self.play.show()

        # Sound Toggle Button
        self.sound = ToolButton("speaker-muted-100")
        self.sound.set_tooltip(_('Mute'))
        self.sound.props.sensitive = True
        self.sound.connect('clicked', self._sound_cb)
        toolbar_box.toolbar.insert(self.sound, -1)
        self.sound.show()

        separator = Gtk.SeparatorToolItem()
        separator.show()
        toolbar_box.toolbar.insert(separator, -1)

        # Label for showing level
        self.level_label = Gtk.Label(label="%s %d" % (_("Level"), 1))
        self.level_label.show()
        level_toolitem = Gtk.ToolItem()
        level_toolitem.add(self.level_label)
        toolbar_box.toolbar.insert(level_toolitem, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        separator.show()
        toolbar_box.toolbar.insert(separator, -1)

        # The ever-present Stop Button
        stop_button = StopButton(self)
        stop_button.props.accelerator = '<Ctrl>Q'
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show()

        # Create a canvas
        canvas = Gtk.DrawingArea()
        canvas.set_size_request(Gdk.Screen.width(), \
                                Gdk.Screen.height())
        self.set_canvas(canvas)
        canvas.show()
        self.show_all()

        # Initialize the canvas
        self.sw = window.new_window(canvas, \
                                    os.path.join(activity.get_bundle_path(), \
                                                 'images/'), \
                                    self)
        self.sw.activity = self

        # Read the level from the Journal
        try:
            sw.level = int(self.metadata['level'])
        except:
            pass

    def _play_cb(self, button):
        window.play_the_game(self.sw)
        return True

    def _sound_cb(self, button):
        if self.sw.sound is True:
            self.sound.set_icon_name("speaker-muted-000")
            self.sound.set_tooltip(_('Unmute'))
            self.sw.sound = False
        else:
            self.sound.set_icon_name("speaker-muted-100")
            self.sound.set_tooltip(_('Mute'))
            self.sw.sound = True
        return True

    """
    Write the slider positions to the Journal
    """

    def write_file(self, file_path):
        _logger.debug("Write level: " + str(self.sw.level))
        self.metadata['level'] = self.sw.level
Ejemplo n.º 10
0
class SideToolbar(Gtk.Toolbar):
    ''' A toolbar on the side of the canvas for adjusting gain/bias '''
    def __init__(self, activity, channel=0):
        ''' Set up initial toolbars '''
        super(type(self), self).__init__()

        self.activity = activity
        self._channel = channel
        if self._channel == 0:
            self.show_toolbar = True
        else:  # Don't show second channel until we need it
            self.show_toolbar = False
        self.mode = 'sound'
        self.mode_values = {'sound': 3, 'sensor': 2}

        self._toggle = Gtk.CheckButton()
        self._toggle.set_active(True)
        self._toggle.connect('clicked', self.toggle_cb)
        self._toggle.show()
        self._toggle_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self._toggle_box.pack_start(self._toggle, False, True, 18)

        self._invert = ToolButton('invert')
        self._invert.set_tooltip(_('Invert'))
        self._invert.connect('clicked', self.invert_control_cb)
        self._invert.show()
        self.activity.wave.set_invert_state(False, channel=self._channel)

        self.button_up = ToolButton('amp-high')
        self.button_up.set_tooltip(_('Increase amplitude'))
        self.button_up.connect('clicked', self._button_up_cb)
        self.button_up.show()

        self.adjustmenty = Gtk.Adjustment(self.mode_values[self.mode], LOWER,
                                          UPPER, 0.1, 0.1, 0.0)
        self.adjustmenty.connect('value_changed', self._yscrollbar_cb,
                                 self.adjustmenty)
        self.yscrollbar = Gtk.Scale(orientation=Gtk.Orientation.VERTICAL,
                                    adjustment=self.adjustmenty)
        self.yscrollbar.set_draw_value(False)
        self.yscrollbar.set_inverted(True)

        self.button_down = ToolButton('amp-low')
        self.button_down.set_tooltip(_('Decrease amplitude'))
        self.button_down.connect('clicked', self._button_down_cb)
        self.button_down.show()

        self.box1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
                            homogeneous=False,
                            spacing=0)

        if self._channel == 0:
            self.box1.pack_start(self._color_wave(self.activity.stroke_color),
                                 False, True, 0)
        elif self._channel == 1:
            self.box1.pack_start(self._color_wave(self.activity.fill_color),
                                 False, True, 0)
        else:
            self.box1.pack_start(self._color_wave('#FFFFFF'), False, True, 0)
        self.box1.pack_start(self._toggle_box, False, True, 0)
        self.box1.pack_start(self._invert, False, True, 0)
        self.box1.pack_start(self.button_up, False, True, 0)
        self.box1.pack_start(self.yscrollbar, True, True, 0)
        self.box1.pack_start(self.button_down, False, True, 0)

        self.set_show_hide(False)

    def _yscrollbar_cb(self, adjy, data=None):
        ''' Callback for scrollbar '''
        if self.mode == 'sound':
            self.activity.wave.set_mag_params(1.0,
                                              adjy.get_value(),
                                              channel=self._channel)
            self.activity.wave.set_bias_param(0, channel=self._channel)
        elif self.mode == 'sensor':
            self.activity.wave.set_bias_param(int(
                300 * (adjy.get_value() - (UPPER - LOWER) / 2.)),
                                              channel=self._channel)
        self.mode_values[self.mode] = adjy.get_value()
        return True

    def _button_up_cb(self, data=None):
        '''Moves slider up'''
        new_value = self.yscrollbar.get_value() + (UPPER - LOWER) \
            / 100.
        if new_value <= UPPER:
            self.yscrollbar.set_value(new_value)
        else:
            self.yscrollbar.set_value(UPPER)
        return True

    def _button_down_cb(self, data=None):
        '''Moves slider down'''
        new_value = self.yscrollbar.get_value() - (UPPER - LOWER) \
            / 100.
        if new_value >= LOWER:
            self.yscrollbar.set_value(new_value)
        else:
            self.yscrollbar.set_value(LOWER)
        return True

    def set_show_hide(self, show=True, mode='sound'):
        ''' Show or hide the toolbar '''
        self.show_toolbar = show
        self.set_side_mode(mode)

    def set_side_mode(self, mode='sound'):
        ''' Set the toolbar to either 'sound' or 'sensor' '''
        self.mode = mode
        if self.mode == 'sound':
            self.button_up.set_icon_name('amp-high')
            self.button_up.set_tooltip(_('Increase amplitude'))
            self.button_down.set_icon_name('amp-low')
            self.button_down.set_tooltip(_('Decrease amplitude'))
        elif self.mode == 'sensor':
            self.button_up.set_icon_name('bias-high')
            self.button_up.set_tooltip(_('Increase bias'))
            self.button_down.set_icon_name('bias-low')
            self.button_down.set_tooltip(_('Decrease bias'))
            self._invert.show()
        self.yscrollbar.set_value(self.mode_values[self.mode])
        return

    def toggle_cb(self, data=None):
        self.activity.wave.set_visibility(self._toggle.get_active(),
                                          channel=self._channel)
        return True

    def invert_control_cb(self, data=None):
        ''' Callback for Invert Button '''
        if self.activity.wave.get_invert_state(channel=self._channel):
            self.activity.wave.set_invert_state(False, self._channel)
            self._invert.set_icon_name('invert')
            self._invert.show()
        elif not self.activity.wave.get_fft_mode():
            self.activity.wave.set_invert_state(True, self._channel)
            self._invert.set_icon_name('invert2')
            self._invert.show()
        self.activity.sensor_toolbar.update_string_for_textbox()
        return False

    def _color_wave(self, color):
        ''' Generate a wave graphic in color to label each side toolbar '''
        svg = '<?xml version="1.0" ?>\n\
<!DOCTYPE svg  PUBLIC "-//W3C//DTD SVG 1.1//EN"\n\
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n\
<svg enable-background="new 0 0 55.125 55" height="55px" version="1.1"\n\
viewBox="0 0 55.125 55" width="55.125px" x="0px" xml:space="preserve"\n\
xmlns="http://www.w3.org/2000/svg"\n\
xmlns:xlink="http://www.w3.org/1999/xlink" y="0px">\n\
<path d="M9.066,27.5 c2.32-6.917,4.666-13.834,9.255-13.834\n\
c9.179,0,9.179,27.668,18.358,27.668c4.59,0,6.986-6.917,9.332-13.834"\n\
fill="none" stroke="%s" stroke-linecap="round" stroke-width="3.5"/>\n\
</svg>' % (color)
        pixbuf = svg_str_to_pixbuf(svg)
        img = Gtk.Image()
        img.set_from_pixbuf(pixbuf)
        img_tool = Gtk.ToolItem()
        img_tool.add(img)
        return img_tool
Ejemplo n.º 11
0
class TuningToolbar(Gtk.Toolbar):
    ''' The toolbar for tuning instruments '''

    def __init__(self, activity):
        super(type(self), self).__init__()

        self.activity = activity
        self._show_tuning_line = False
        self._updating_note = True
        self._tuning_tool = None

        self._instrument_button = ToolButton('instruments')
        self._instrument_button.set_tooltip(_('Tune an instrument.'))
        self._instrument_button.connect('clicked',
                                        self._button_selection_cb)
        self.insert(self._instrument_button, -1)
        self._setup_instrument_palette()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = True
        self.insert(separator, -1)

        self._note = 'A'
        self._notes_button = ToolButton('notes')
        self._notes_button.set_tooltip(_('Notes'))
        self._notes_button.connect('clicked',
                                   self._button_selection_cb)
        self.insert(self._notes_button, -1)
        self._setup_notes_palette()

        self._octave = 4
        self._octaves_button = ToolButton('octaves')
        self._octaves_button.set_tooltip(_('Octaves'))
        self._octaves_button.connect('clicked',
                                     self._button_selection_cb)
        self.insert(self._octaves_button, -1)
        self._setup_octaves_palette()

        # The entry is used to display a note or for direct user input
        self._freq_entry = Gtk.Entry()
        self._freq_entry.set_text('440')  # A
        self._freq_entry_changed_id = self._freq_entry.connect(
            'changed', self._update_freq_entry)
        if hasattr(self._freq_entry, 'set_tooltip_text'):
            self._freq_entry.set_tooltip_text(
                _('Enter a frequency to display.'))
        self._freq_entry.set_width_chars(8)
        self._freq_entry.show()
        toolitem = Gtk.ToolItem()
        toolitem.add(self._freq_entry)
        self.insert(toolitem, -1)
        toolitem.show()

        self._new_tuning_line = ToolButton('tuning-tools')
        self._new_tuning_line.show()
        self.insert(self._new_tuning_line, -1)
        self._new_tuning_line.set_tooltip(_('Show tuning line.'))
        self._new_tuning_line.connect('clicked', self.tuning_line_cb)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = True
        self.insert(separator, -1)

        self._harmonic = ToolButton('harmonics')
        self._harmonic.show()
        self.insert(self._harmonic, -1)
        self._harmonic.set_tooltip(_('Show harmonics.'))
        self._harmonic.connect('clicked', self.harmonic_cb)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = True
        self.insert(separator, -1)

        self._play_tone = ToolButton('media-playback-start')
        self._play_tone.show()
        self.insert(self._play_tone, -1)
        self._play_tone.set_tooltip(_('Play a note.'))
        self._play_tone.connect('clicked', self.play_cb)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self.insert(separator, -1)

        self.label = Gtk.Label(label='')
        self.label.set_use_markup(True)
        self.label.show()
        toolitem = Gtk.ToolItem()
        toolitem.add(self.label)
        self.insert(toolitem, -1)
        toolitem.show()

        self.show_all()

    def _update_note(self):
        ''' Calculate the frequency based on note and octave '''
        if not hasattr(self, '_freq_entry'):  # Still setting up toolbar
            return
        i = self._octave * 12 + NOTES.index(self._note)
        freq = A0 * pow(TWELTHROOT2, i)
        self._updating_note = True
        self._freq_entry.set_text('%0.3f' % (freq))
        self.label.set_markup(SPAN % (style.COLOR_WHITE.get_html(),
                                      self._note + str(self._octave)))
        if self._show_tuning_line:
            self.activity.wave.tuning_line = freq
        return

    def _update_freq_entry(self, widget):
        # Calculate a note from a frequency
        if not self._updating_note:  # Only if user types in a freq.
            try:
                freq = float(self._freq_entry.get_text())
                # Only consider notes in piano range
                if freq < A0 * 0.97:
                    self.label.set_text('< A0')
                    return
                if freq > C8 * 1.03:
                    self.label.set_text('> C8')
                    return
                self.label.set_markup(freq_note(freq, flatsharp=True))
            except ValueError:
                return

        self._updating_note = False

    def _button_selection_cb(self, widget):
        palette = widget.get_palette()
        if palette:
            if not palette.is_up():
                palette.popup(immediate=True)
            else:
                palette.popdown(immediate=True)
            return

    def _setup_notes_palette(self):
        self._notes_palette = self._notes_button.get_palette()

        for note in NOTES:
            menu_item = MenuItem(icon_name='',
                                 text_label=note)
            menu_item.connect('activate', self._note_selected_cb, note)
            self._notes_palette.menu.append(menu_item)
            menu_item.show()

    def _note_selected_cb(self, widget, note):
        self._note = note
        self._update_note()

    def _setup_octaves_palette(self):
        self._octaves_palette = self._octaves_button.get_palette()

        for octave in range(9):
            menu_item = MenuItem(icon_name='',
                                 text_label=str(octave))
            menu_item.connect('activate', self._octave_selected_cb, octave)
            self._octaves_palette.menu.append(menu_item)
            menu_item.show()

    def _octave_selected_cb(self, widget, octave):
        self._octave = octave
        self._update_note()

    def _setup_instrument_palette(self):
        self.instrument_palette = self._instrument_button.get_palette()

        self.instrument = []
        for k in INSTRUMENT_DICT.keys():
            self.instrument.append(k)
            menu_item = MenuItem(icon_name='',
                                 text_label=k)
            menu_item.connect('activate', self.instrument_selected_cb, k)
            self.instrument_palette.menu.append(menu_item)
            menu_item.show()

    def instrument_selected_cb(self, button, instrument):
        ''' Callback for instrument control '''
        logging.debug(instrument)
        if self._tuning_tool is not None:
            self.remove(self._tuning_tool)

        if instrument == _('None'):
            self.activity.wave.instrument = None

            # Remove any previous tuning button
            if hasattr(self, '_tuning_button'):
                self._tuning_button.destroy()

            # Restore the notes, octaves buttons
            if hasattr(self, '_notes_button'):
                self.insert(self._notes_button, 2)
                self.insert(self._octaves_button, 3)
            return

        self.remove(self._notes_button)
        self.remove(self._octaves_button)

        self.activity.wave.instrument = instrument

        # If we are not already in freq. base, switch.
        if not self.activity.wave.get_fft_mode():
            self.activity.timefreq_control()

        # Add a Tuning palette for this instrument
        self._tuning_button = ToolButton('notes')
        self._tuning_button.set_tooltip(instrument)
        self._tuning_button.connect('clicked', self._button_selection_cb)
        self.insert(self._tuning_button, 1)
        self._setup_tuning_palette(instrument)

    def _setup_tuning_palette(self, instrument):
        self._tuning_palette = self._tuning_button.get_palette()

        self.tuning = []
        self.tuning.append(_('All notes'))
        menu_item = MenuItem(icon_name='', text_label=_('All notes'))
        menu_item.connect('activate', self._tuning_selected_cb,
                          instrument, -1)
        self._tuning_palette.menu.append(menu_item)
        menu_item.show()

        for i, f in enumerate(INSTRUMENT_DICT[instrument]):
            self.tuning.append(freq_note(f))
            menu_item = MenuItem(icon_name='',
                                 text_label=freq_note(f))
            menu_item.connect('activate', self._tuning_selected_cb,
                              instrument, i)
            self._tuning_palette.menu.append(menu_item)
            menu_item.show()

        self.show_all()

    def _tuning_selected_cb(self, widget, instrument, fidx):
        ''' Update note '''
        if not hasattr(self, '_freq_entry'):  # Still setting up toolbar?
            return

        if instrument not in INSTRUMENT_DICT:
            return

        if fidx == -1:  # All notes
            self.activity.wave.instrument = instrument
            self.activity.wave.tuning_line = 0.0
            self._new_tuning_line.set_icon('tuning-tools')
            self._new_tuning_line.set_tooltip(_('Show tuning line.'))
            self._show_tuning_line = False
        else:
            freq = INSTRUMENT_DICT[instrument][fidx]
            self.activity.wave.instrument = None
            self.activity.wave.tuning_line = freq
            self._new_tuning_line.set_icon('tuning-tools-off')
            self._new_tuning_line.set_tooltip(_('Hide tuning line.'))
            self._show_tuning_line = True

        self._updating_note = False

    def harmonic_cb(self, *args):
        ''' Callback for harmonics control '''
        self.activity.wave.harmonics = not self.activity.wave.harmonics
        if self.activity.wave.harmonics:
            self._harmonic.set_icon_name('harmonics-off')
            self._harmonic.set_tooltip(_('Hide harmonics.'))
            if self.activity.wave.instrument is None and \
               self.activity.wave.tuning_line == 0.0:
                self._load_tuning_line()
        else:
            self._harmonic.set_icon_name('harmonics')
            self._harmonic.set_tooltip(_('Show harmonics.'))

    def tuning_line_cb(self, *args):
        ''' Callback for tuning insert '''
        if self._show_tuning_line:
            self.activity.wave.tuning_line = 0.0
            self._new_tuning_line.set_icon_name('tuning-tools')
            self._new_tuning_line.set_tooltip(_('Show tuning line.'))
            self._show_tuning_line = False
        else:
            self._load_tuning_line()

    def _load_tuning_line(self):
        ''' Read the freq entry and use value to set tuning line '''
        freq = self._freq_entry.get_text()
        try:
            self.activity.wave.tuning_line = float(freq)
            if freq < 0:
                freq = -freq
            self._new_tuning_line.set_icon_name('tuning-tools-off')
            self._new_tuning_line.set_tooltip(_('Hide tuning line.'))
            self._show_tuning_line = True
        except ValueError:
            self.activity.wave.tuning_line = 0.0
            self._freq_entry.set_text('0')
        # If we are not already in freq. base, switch.
        if not self.activity.wave.get_fft_mode():
            self.activity.timefreq_control()

    def play_cb(self, *args):
        ''' Save settings, turn off display, and then play a tone at
        the current frequency '''
        channels = []
        for c in range(self.activity.audiograb.channels):
            channels.append(self.activity.wave.get_visibility(channel=c))
            self.activity.wave.set_visibility(False, channel=c)
        wave_status = self.activity.wave.get_active()
        self.activity.wave.set_context_off()
        self.activity.wave.set_active(False)
        if self.activity.hw in [XO4, XO175]:
            self.activity.audiograb.stop_grabbing()

        freq = float(self._freq_entry.get_text())
        GLib.timeout_add(200, self.play_sound, freq, channels, wave_status)

    def play_sound(self, freq, channels, wave_status):
        ''' Play the sound and then restore wave settings '''
        self._play_sinewave(freq, 5000, 1)

        if self.activity.hw in [XO4, XO175]:
            self.activity.sensor_toolbar.set_mode('sound')
            self.activity.sensor_toolbar.set_sound_context()
            self.activity.audiograb.start_grabbing()
        for c in range(self.activity.audiograb.channels):
            self.activity.wave.set_visibility(channels[c], channel=c)
        self.activity.wave.set_context_on()
        self.activity.wave.set_active(wave_status)

    def _play_sinewave(self, pitch, amplitude=5000, duration=1):
        """ Create a Csound score to play a sine wave. """
        self.orchlines = []
        self.scorelines = []
        self.instrlist = []

        try:
            pitch = abs(float(pitch))
            amplitude = abs(float(amplitude))
            duration = abs(float(duration))
        except ValueError:
            logging.error('bad args to _play_sinewave')
            return

        self._prepare_sinewave(pitch, amplitude, duration)

        path = os.path.join(self.activity.get_activity_root(), 'instance',
                            'tmp.csd')
        # Create a csound file from the score.
        self._audio_write(path)

        # Play the csound file.
        check_output(['csound', path], 'call to csound failed?')

    def _prepare_sinewave(self, pitch, amplitude, duration, starttime=0,
                          pitch_envelope=99, amplitude_envelope=100,
                          instrument=1):

        pitenv = pitch_envelope
        ampenv = amplitude_envelope
        if 1 not in self.instrlist:
            self.orchlines.append("instr 1\n")
            self.orchlines.append("kpitenv oscil 1, 1/p3, p6\n")
            self.orchlines.append("aenv oscil 1, 1/p3, p7\n")
            self.orchlines.append("asig oscil p5*aenv, p4*kpitenv, p8\n")
            self.orchlines.append("out asig\n")
            self.orchlines.append("endin\n\n")
            self.instrlist.append(1)

        self.scorelines.append("i1 %s %s %s %s %s %s %s\n" %
                               (str(starttime), str(duration), str(pitch),
                                str(amplitude), str(pitenv), str(ampenv),
                                str(instrument)))

    def _audio_write(self, file):
        """ Compile a .csd file. """

        csd = open(file, "w")
        csd.write("<CsoundSynthesizer>\n\n")
        csd.write("<CsOptions>\n")
        csd.write("-+rtaudio=alsa -odevaudio -m0 -d -b256 -B512\n")
        csd.write("</CsOptions>\n\n")
        csd.write("<CsInstruments>\n\n")
        csd.write("sr=16000\n")
        csd.write("ksmps=50\n")
        csd.write("nchnls=1\n\n")
        for line in self.orchlines:
            csd.write(line)
        csd.write("\n</CsInstruments>\n\n")
        csd.write("<CsScore>\n\n")
        csd.write("f1 0 2048 10 1\n")
        csd.write("f2 0 2048 10 1 0 .33 0 .2 0 .143 0 .111\n")
        csd.write("f3 0 2048 10 1 .5 .33 .25 .2 .175 .143 .125 .111 .1\n")
        csd.write("f10 0 2048 10 1 0 0 .3 0 .2 0 0 .1\n")
        csd.write("f99 0 2048 7 1 2048 1\n")
        csd.write("f100 0 2048 7 0. 10 1. 1900 1. 132 0.\n")
        csd.write(self.scorelines.pop())
        csd.write("e\n")
        csd.write("\n</CsScore>\n")
        csd.write("\n</CsoundSynthesizer>")
        csd.close()
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self.set_title('FotoToon')

        self._max_participants = 1

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        activity_toolbar = activity_button.page
        toolbar_box.toolbar.insert(activity_button, 0)

        edit_toolbar_btn = ToolbarButton()
        edit_toolbar = Gtk.Toolbar()
        edit_toolbar_btn.props.page = edit_toolbar
        edit_toolbar_btn.props.icon_name = 'toolbar-edit'
        edit_toolbar_btn.label = _('Edit')
        toolbar_box.toolbar.insert(edit_toolbar_btn, -1)

        view_toolbar_btn = ToolbarButton()
        view_toolbar = Gtk.Toolbar()
        view_toolbar_btn.props.page = view_toolbar
        view_toolbar_btn.props.icon_name = 'toolbar-view'
        view_toolbar_btn.label = _('View')
        toolbar_box.toolbar.insert(view_toolbar_btn, -1)

        slideview_btn = ToggleToolButton('slideshow')
        slideview_btn.set_tooltip(_('Slideshow'))
        slideview_btn.set_active(False)
        view_toolbar.insert(slideview_btn, -1)
        slideview_btn.show()

        fullscreen_btn = ToolButton('view-fullscreen')
        fullscreen_btn.set_tooltip(_('Fullscreen'))
        fullscreen_btn.props.accelerator = '<Alt>Return'
        fullscreen_btn.connect('clicked', lambda w: self.fullscreen())
        view_toolbar.insert(fullscreen_btn, -1)
        fullscreen_btn.show()

        self.set_toolbar_box(toolbar_box)

        toolbar = toolbar_box.toolbar

        self.page = Page()

        self.globes_manager = GlobesManager(toolbar, edit_toolbar, self)

        # fonts
        text_button = ToolbarButton()
        text_button.props.page = TextToolbar(self.page)
        text_button.props.icon_name = 'format-text-size'
        text_button.props.label = _('Text')
        slideview_btn.connect('clicked', self._switch_view_mode, text_button)
        toolbar_box.toolbar.insert(text_button, -1)

        reorder_img_btn = ToolButton('thumbs-view')
        reorder_img_btn.set_icon_name('thumbs-view')
        reorder_img_btn.set_tooltip(_('Change image order'))
        reorder_img_btn.connect('clicked', self.__image_order_cb)
        edit_toolbar.insert(reorder_img_btn, -1)
        reorder_img_btn.show()

        bgchange = ToolButton(icon_name='contract-coordinates')
        bgchange.set_tooltip(_('Edit background image'))
        bgchange.connect('clicked', self.__bgchange_clicked_cb)
        edit_toolbar.insert(bgchange, -1)
        bgchange.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)

        toolbar_box.toolbar.insert(separator, -1)

        stop = StopButton(self)
        toolbar_box.toolbar.insert(stop, -1)

        toolbar_box.show_all()

        # add export button

        separator_2 = Gtk.SeparatorToolItem()
        separator_2.show()
        activity_toolbar.insert(separator_2, -1)

        self.bt_save_as_image = ToolButton()
        self.bt_save_as_image.props.icon_name = 'save-as-image'
        self.bt_save_as_image.connect('clicked', self.write_image)
        self.bt_save_as_image.set_tooltip(_('Save as Image'))
        activity_toolbar.insert(self.bt_save_as_image, -1)
        self.bt_save_as_image.show()

        save_as_pdf = ToolButton()
        save_as_pdf.props.icon_name = 'save-as-pdf'
        save_as_pdf.connect('clicked', self._save_as_pdf)
        save_as_pdf.set_tooltip(_('Save as a Book (PDF)'))
        activity_toolbar.insert(save_as_pdf, -1)
        save_as_pdf.show()

        activity_button.page.title.connect("focus-in-event", self.on_title)

        scrolled = Gtk.ScrolledWindow()
        #scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.ALWAYS)
        scrolled.add_with_viewport(self.page)
        scrolled.set_kinetic_scrolling(False)
        scrolled.show_all()

        self._slideview = SlideView(self)
        self._slideview.show_all()

        self._notebook = Gtk.Notebook()
        self._notebook.set_show_tabs(False)
        self._notebook.append_page(scrolled, None)
        self._notebook.append_page(self._slideview, None)
        self._notebook.show_all()

        self.set_canvas(self._notebook)
        self.show()
        self.metadata['mime_type'] = 'application/x-fototoon-activity'

        self.page.empty_page = handle.object_id is None
        self._key_press_signal_id = None
Ejemplo n.º 13
0
class ErikosActivity(activity.Activity):

    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self.max_participants = 1

        toolbar_box = ToolbarBox()

        # Buttons added to the Activity toolbar
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        # Play Button
        self.play = ToolButton( "media-playback-start" )
        self.play.set_tooltip(_('Play'))
        self.play.props.sensitive = True
        self.play.connect('clicked', self._play_cb)
        toolbar_box.toolbar.insert(self.play, -1)
        self.play.show()

        # Sound Toggle Button
        self.sound = ToolButton( "speaker-muted-100" )
        self.sound.set_tooltip(_('Mute'))
        self.sound.props.sensitive = True
        self.sound.connect('clicked', self._sound_cb)
        toolbar_box.toolbar.insert(self.sound, -1)
        self.sound.show()

        separator = Gtk.SeparatorToolItem()
        separator.show()
        toolbar_box.toolbar.insert(separator, -1)

        # Label for showing level
        self.level_label = Gtk.Label(label="%s %d" % (_("Level"),1))
        self.level_label.show()
        level_toolitem = Gtk.ToolItem()
        level_toolitem.add(self.level_label)
        toolbar_box.toolbar.insert(level_toolitem,-1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        separator.show()
        toolbar_box.toolbar.insert(separator, -1)

        # The ever-present Stop Button
        stop_button = StopButton(self)
        stop_button.props.accelerator = '<Ctrl>Q'
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show()

        # Create a canvas
        canvas = Gtk.DrawingArea()
        canvas.set_size_request(Gdk.Screen.width(), \
                                Gdk.Screen.height())
        self.set_canvas(canvas)
        canvas.show()
        self.show_all()

        # Initialize the canvas
        self.sw = window.new_window(canvas, \
                                    os.path.join(activity.get_bundle_path(), \
                                                 'images/'), \
                                    self)
        self.sw.activity = self

        # Read the level from the Journal
        try:
            sw.level = int(self.metadata['level'])
        except:
            pass

    def _play_cb(self, button):
        window.play_the_game(self.sw)
        return True

    def _sound_cb(self, button):
        if self.sw.sound is True:
            self.sound.set_icon_name("speaker-muted-000")
            self.sound.set_tooltip(_('Unmute'))
            self.sw.sound = False
        else:
            self.sound.set_icon_name("speaker-muted-100")
            self.sound.set_tooltip(_('Mute'))
            self.sw.sound = True
        return True

    """
    Write the slider positions to the Journal
    """
    def write_file(self, file_path):
        _logger.debug("Write level: " + str(self.sw.level))
        self.metadata['level'] = self.sw.level
Ejemplo n.º 14
0
class TrainingActivity(activity.Activity):
    ''' A series of training exercises '''
    transfer_started_signal = GObject.Signal('started', arg_types=([]))
    transfer_progressed_signal = GObject.Signal('progressed', arg_types=([]))
    transfer_completed_signal = GObject.Signal('completed', arg_types=([]))
    transfer_failed_signal = GObject.Signal('failed', arg_types=([]))

    def __init__(self, handle):
        ''' Initialize the toolbars and the game board '''
        try:
            super(TrainingActivity, self).__init__(handle)
        except dbus.exceptions.DBusException as e:
            _logger.error(str(e))

        self.connect('realize', self.__realize_cb)
        self.connect('started', self.__transfer_started_cb)
        self.connect('progressed', self.__transfer_progressed_cb)
        self.connect('completed', self.__transfer_completed_cb)
        self.connect('failed', self.__transfer_failed_cb)

        self.volume_monitor = Gio.VolumeMonitor.get()
        self.volume_monitor.connect('mount-added', self._mount_added_cb)
        self.volume_monitor.connect('mount-removed', self._mount_removed_cb)

        if hasattr(self, 'metadata') and 'font_size' in self.metadata:
            self.font_size = int(self.metadata['font_size'])
        else:
            self.font_size = 8
        self.zoom_level = self.font_size / float(len(FONT_SIZES))
        _logger.debug('zoom level is %f' % self.zoom_level)

        _check_gconf_settings()  # For debugging purposes

        self._setup_toolbars()
        self.modify_bg(Gtk.StateType.NORMAL,
                       style.COLOR_WHITE.get_gdk_color())

        self.bundle_path = activity.get_bundle_path()
        self.volume_data = []

        self.help_palette = None
        self.help_panel_visible = False
        self._copy_entry = None
        self._paste_entry = None
        self._webkit = None
        self._clipboard_text = ''
        self._fixed = None
        self._notify_transfer_status = False

        if self._load_extension() and self.check_volume_data():
            self._launcher()

    def _launcher(self):
        get_power_manager().inhibit_suspend()

        # We are resuming the activity or we are launching a new instance?
        # * Is there a data file to sync on the USB key?
        # * Do we create a new data file on the USB key?
        path = self._check_for_USB_data()
        if path is None:
            self._launch_task_master()
        elif self._sync_data_from_USB(path):
            self._copy_data_from_USB()
            # Flash a welcome back screen.
            self._load_intro_graphics(file_name='welcome-back.html')
            GObject.timeout_add(1500, self._launch_task_master)

    def can_close(self):
        get_power_manager().restore_suspend()
        return True

    def busy_cursor(self):
        self._old_cursor = self.get_window().get_cursor()
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))

    def reset_cursor(self):
        if hasattr(self, '_old_cursor'):
            self.get_window().set_cursor(self._old_cursor)

    def check_volume_data(self):
        # Before we begin (and before each task),
        # we need to find any and all USB keys
        # and any and all training-data files on them.

        _logger.debug(utils.get_volume_paths())
        self.volume_data = []
        for path in utils.get_volume_paths():
            os.path.basename(path)
            self.volume_data.append(
                {'basename': os.path.basename(path),
                 'files': utils.look_for_training_data(path),
                 'sugar_path': os.path.join(self.get_activity_root(), 'data'),
                 'usb_path': path})
            _logger.debug(self.volume_data[-1])

        # (1) We require a USB key
        if len(self.volume_data) == 0:
            _logger.error('NO USB KEY INSERTED')
            alert = ConfirmationAlert()
            alert.props.title = _('USB key required')
            alert.props.msg = _('You must insert a USB key before launching '
                                'this activity.')
            alert.connect('response', self._remove_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(file_name='insert-usb.html')
            return False

        # (2) Only one USB key
        if len(self.volume_data) > 1:
            _logger.error('MULTIPLE USB KEYS INSERTED')
            alert = ConfirmationAlert()
            alert.props.title = _('Multiple USB keys found')
            alert.props.msg = _('Only one USB key must be inserted while '
                                'running this program.\nPlease remove any '
                                'additional USB keys before launching '
                                'this activity.')
            alert.connect('response', self._remove_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(message=alert.props.msg)
            return False

        volume = self.volume_data[0]

        # (3) At least 10MB of free space
        if utils.is_full(volume['usb_path'],
                         required=_MINIMUM_SPACE):
            _logger.error('USB IS FULL')
            alert = ConfirmationAlert()
            alert.props.title = _('USB key is full')
            alert.props.msg = _('No room on USB')
            alert.connect('response', self._close_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(message=alert.props.msg)
            return False

        # (4) File is read/write
        if not utils.is_writeable(volume['usb_path']):
            _logger.error('CANNOT WRITE TO USB')
            alert = ConfirmationAlert()
            alert.props.title = _('Cannot write to USB')
            alert.props.msg = _('USB key seems to be read-only.')
            alert.connect('response', self._close_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(message=alert.props.msg)
            return False

        # (5) Only one set of training data per USB key
        # We expect UIDs to formated as XXXX-XXXX
        # We need to make sure we have proper UIDs associated with
        # the USBs and the files on them match the UID.
        # (a) If there are no files, we will assign the UID based on the
        #     volume path;
        # (b) If there is one file with a valid UID, we use that UID;
        if len(volume['files']) == 0:
            volume['uid'] = 'training-data-%s' % \
                            utils.format_volume_name(volume['basename'])
            _logger.debug('No training data found. Using UID %s' %
                          volume['uid'])
            return True
        elif len(volume['files']) == 1:
            volume['uid'] = 'training-data-%s' % volume['files'][0][-9:]
            _logger.debug('Training data found. Using UID %s' %
                          volume['uid'])
            return True
        else:
            _logger.error('MULTIPLE TRAINING-DATA FILES FOUND')
            alert = ConfirmationAlert()
            alert.props.title = _('Multiple training-data files found.')
            alert.props.msg = _('There can only be one set of training '
                                'data per USB key.')
            alert.connect('response', self._close_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(message=alert.props.msg)
            return False

    def _check_for_USB_data(self):
        usb_path = os.path.join(self.volume_data[0]['usb_path'],
                                self.volume_data[0]['uid'])
        if os.path.exists(usb_path):
            return usb_path
        else:
            return None

    def _sync_data_from_USB(self, usb_data_path=None):
        # We need to sync up file on USB with file on disk,
        # but only if the email addresses match. Otherwise,
        # raise an error.
        if usb_data_path is not None:
            usb_data = {}
            if os.path.exists(usb_data_path):
                fd = open(usb_data_path, 'r')
                json_data = fd.read()
                fd.close()
                if len(json_data) > 0:
                    try:
                        usb_data = json.loads(json_data)
                    except ValueError as e:
                        _logger.error('Cannot load USB data: %s' % e)
            else:
                _logger.error('Cannot find USB data: %s' % usb_data_path)

            sugar_data_path = os.path.join(
                self.volume_data[0]['sugar_path'],
                self.volume_data[0]['uid'])
            sugar_data = {}
            if os.path.exists(sugar_data_path):
                fd = open(sugar_data_path, 'r')
                json_data = fd.read()
                fd.close()
                if len(json_data) > 0:
                    try:
                        sugar_data = json.loads(json_data)
                    except ValueError as e:
                        _logger.error('Cannot load Sugar data: %s' % e)
            else:
                _logger.error('Cannot find Sugar data: %s' % sugar_data_path)

            # First, check to make sure email_address matches
            if EMAIL_UID in usb_data:
                usb_email = usb_data[EMAIL_UID]
            else:
                usb_email = None
            if EMAIL_UID in sugar_data:
                sugar_email = sugar_data[EMAIL_UID]
            else:
                sugar_email = None
            if usb_email != sugar_email:
                if usb_email is None and sugar_email is not None:
                    _logger.warning('Using email address from Sugar: %s' %
                                    sugar_email)
                    usb_data[EMAIL_UID] = sugar_email
                elif usb_email is not None and sugar_email is None:
                    _logger.warning('Using email address from USB: %s' %
                                    usb_email)
                    sugar_data[EMAIL_UID] = usb_email
                elif usb_email is None and sugar_email is None:
                    _logger.warning('No email address found')
                else:
                    # FIX ME: We need to resolve this, but for right now, punt.
                    alert = ConfirmationAlert()
                    alert.props.title = _('Data mismatch')
                    alert.props.msg = _('Are you %(usb)s or %(sugar)s?' %
                                        {'usb': usb_email,
                                         'sugar': sugar_email})
                    alert.connect('response', self._close_alert_cb)
                    self.add_alert(alert)
                    self._load_intro_graphics(message=alert.props.msg)
                    return False

            def count_completed(data):
                count = 0
                for key in data:
                    if isinstance(data[key], dict) and \
                       'completed' in data[key] and \
                       data[key]['completed']:
                        count += 1
                return count

            # The database with the most completed tasks takes precedence.
            if count_completed(usb_data) >= count_completed(sugar_data):
                _logger.debug('data sync: USB data takes precedence')
                data_one = usb_data
                data_two = sugar_data
            else:
                _logger.debug('data sync: Sugar data takes precedence')
                data_one = sugar_data
                data_two = usb_data

            # Copy completed tasks from one to two
            for key in data_one:
                if isinstance(data_one[key], dict) and \
                   'completed' in data_one[key] and \
                   data_one[key]['completed']:
                    data_two[key] = data_one[key]

            # Copy completed tasks from two to one
            for key in data_two:
                if isinstance(data_two[key], dict) and \
                   'completed' in data_two[key] and \
                   data_two[key]['completed']:
                    data_one[key] = data_two[key]

            # Copy incompleted tasks from one to two
            for key in data_one:
                if isinstance(data_one[key], dict) and \
                   (not 'completed' in data_one[key] or
                    not data_one[key]['completed']):
                        data_two[key] = data_one[key]

            # Copy incompleted tasks from two to one
            for key in data_two:
                if isinstance(data_two[key], dict) and \
                   (not 'completed' in data_two[key] or
                    not data_two[key]['completed']):
                        data_one[key] = data_two[key]

            # Copy name, email_address, current_task...
            for key in data_one:
                if not isinstance(data_one[key], dict):
                    data_two[key] = data_one[key]
            for key in data_two:
                if not isinstance(data_two[key], dict):
                    data_one[key] = data_two[key]

            # Finally, write to the USB and ...
            json_data = json.dumps(data_one)
            fd = open(usb_data_path, 'w')
            fd.write(json_data)
            fd.close()

            # ...save a shadow copy in Sugar
            fd = open(sugar_data_path, 'w')
            fd.write(json_data)
            fd.close()
            return True
        else:
            _logger.error('No data to sync on USB')
            return False

    def _copy_data_from_USB(self):
        usb_path = self._check_for_USB_data()
        if usb_path is not None:
            try:
                subprocess.call(['cp', usb_path,
                                 self.volume_data[0]['sugar_path']])
            except OSError as e:
                _logger.error('Could not copy %s to %s: %s' % (
                    usb_path, self.volume_data[0]['sugar_path'], e))
        else:
            _logger.error('No data found on USB')

    def toolbar_expanded(self):
        if self.activity_button.is_expanded():
            return True
        elif self.edit_toolbar_button.is_expanded():
            return True
        elif self.view_toolbar_button.is_expanded():
            return True
        elif hasattr(self, 'progress_toolbar_button') and \
             self.progress_toolbar_button.is_expanded():
            return True

    def _launch_task_master(self):
        # Most things need only be done once
        if self._fixed is None:
            self._fixed = Gtk.Fixed()
            self._fixed.set_size_request(Gdk.Screen.width(),
                                         Gdk.Screen.height())

            # Offsets from the bottom of the screen
            dy1 = 3 * style.GRID_CELL_SIZE
            dy2 = 2 * style.GRID_CELL_SIZE

            self._progress_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._progress_area.set_size_request(Gdk.Screen.width(), -1)
            self._fixed.put(self._progress_area, 0, Gdk.Screen.height() - dy2)
            self._progress_area.show()

            self._button_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._button_area.set_size_request(Gdk.Screen.width(), -1)
            self._fixed.put(self._button_area, 0, Gdk.Screen.height() - dy1)
            self._button_area.show()

            self._scrolled_window = Gtk.ScrolledWindow()
            self._scrolled_window.set_size_request(
                Gdk.Screen.width(), Gdk.Screen.height() - dy1)
            self._set_scroll_policy()
            self._graphics_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._scrolled_window.add_with_viewport(self._graphics_area)
            self._graphics_area.show()
            self._fixed.put(self._scrolled_window, 0, 0)
            self._scrolled_window.show()

            self._task_master = TaskMaster(self)
            self._task_master.show()

            # Now that we have the tasks, we can build the progress toolbar and
            # help panel.
            self._build_progress_toolbar()

            self._help_panel = HelpPanel(self._task_master)
            self.help_palette = self._help_button.get_palette()
            self.help_palette.set_content(self._help_panel)
            self._help_panel.show()
            self._help_button.set_sensitive(True)

            Gdk.Screen.get_default().connect('size-changed',
                                             self._configure_cb)
            self._toolbox.connect('hide', self._resize_hide_cb)
            self._toolbox.connect('show', self._resize_show_cb)

            self._task_master.set_events(Gdk.EventMask.KEY_PRESS_MASK)
            self._task_master.connect('key_press_event',
                                      self._task_master.keypress_cb)
            self._task_master.set_can_focus(True)
            self._task_master.grab_focus()

        self.set_canvas(self._fixed)
        self._fixed.show()

        self.completed = False
        self._update_completed_sections()
        self._check_connected_task_status()
        self._task_master.task_master()

    def load_graphics_area(self, widget):
        self._graphics_area.add(widget)

    def load_button_area(self, widget):
        self._button_area.add(widget)

    def load_progress_area(self, widget):
        self._progress_area.add(widget)

    def _load_intro_graphics(self, file_name='generic-problem.html',
                             message=None):
        center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0)
        url = os.path.join(self.bundle_path, 'html-content', file_name)
        graphics = Graphics()
        if message is None:
            graphics.add_uri('file://' + url)
        else:
            graphics.add_uri('file://' + url + '?MSG=' +
                             utils.get_safe_text(message))
        graphics.set_zoom_level(0.667)
        center_in_panel.add(graphics)
        graphics.show()
        self.set_canvas(center_in_panel)
        center_in_panel.show()

    def _resize_hide_cb(self, widget):
        self._resize_canvas(widget, True)

    def _resize_show_cb(self, widget):
        self._resize_canvas(widget, False)

    def _configure_cb(self, event):
        self._fixed.set_size_request(Gdk.Screen.width(), Gdk.Screen.height())
        self._set_scroll_policy()
        self._resize_canvas(None)
        self._task_master.reload_graphics()

    def _resize_canvas(self, widget, fullscreen=False):
        # When a toolbar is expanded or collapsed, resize the canvas
        # to ensure that the progress bar is still visible.
        if hasattr(self, '_task_master'):
            if self.toolbar_expanded():
                dy1 = 4 * style.GRID_CELL_SIZE
                dy2 = 3 * style.GRID_CELL_SIZE
            else:
                dy1 = 3 * style.GRID_CELL_SIZE
                dy2 = 2 * style.GRID_CELL_SIZE

            if fullscreen:
                dy1 -= 2 * style.GRID_CELL_SIZE
                dy2 -= 2 * style.GRID_CELL_SIZE

            self._scrolled_window.set_size_request(
                Gdk.Screen.width(), Gdk.Screen.height() - dy1)
            self._fixed.move(self._progress_area, 0, Gdk.Screen.height() - dy2)
            self._fixed.move(self._button_area, 0, Gdk.Screen.height() - dy1)

        self.help_panel_visible = False

    def get_activity_version(self):
        info_path = os.path.join(self.bundle_path, 'activity', 'activity.info')
        try:
            info_file = open(info_path, 'r')
        except Exception as e:
            _logger.error('Could not open %s: %s' % (info_path, e))
            return 'unknown'

        cp = ConfigParser()
        cp.readfp(info_file)

        section = 'Activity'

        if cp.has_option(section, 'activity_version'):
            activity_version = cp.get(section, 'activity_version')
        else:
            activity_version = 'unknown'
        return activity_version

    def get_uid(self):
        if len(self.volume_data) == 1:
            return self.volume_data[0]['uid']
        else:
            return 'unknown'

    def write_file(self, file_path):
        # Only write if we have a valid USB/data file to work with.
        if len(self.volume_data) == 1 and \
           len(self.volume_data[0]['files']) == 1:
            self.metadata[TRAINING_DATA_UID] = self.volume_data[0]['uid']

            # We may have failed before getting to init of taskmaster
            if hasattr(self, '_task_master'):
                self._task_master.write_task_data(
                    'current_task', self._task_master.current_task)
                self.update_activity_title()
                email = self._task_master.read_task_data(EMAIL_UID)
                if email is None:
                    email = ''
                self.metadata[TRAINING_DATA_EMAIL] = email
                name = self._task_master.read_task_data(NAME_UID)
                if name is None:
                    name = ''
                self.metadata[TRAINING_DATA_FULLNAME] = name

        self.metadata['font_size'] = str(self.font_size)

    def update_activity_title(self):
        name = self._task_master.read_task_data(NAME_UID)
        if name is not None:
            bundle_name = activity.get_bundle_name()
            self.metadata['title'] = _('%(name)s %(bundle)s Activity') % \
                {'name': name, 'bundle': bundle_name}

    def _setup_toolbars(self):
        ''' Setup the toolbars. '''
        self.max_participants = 1  # No sharing

        self._toolbox = ToolbarBox()

        self.activity_button = ActivityToolbarButton(self)
        self.activity_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.activity_button, 0)
        self.activity_button.show()

        self.set_toolbar_box(self._toolbox)
        self._toolbox.show()
        self.toolbar = self._toolbox.toolbar

        view_toolbar = Gtk.Toolbar()
        self.view_toolbar_button = ToolbarButton(
            page=view_toolbar,
            label=_('View'),
            icon_name='toolbar-view')
        self.view_toolbar_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.view_toolbar_button, 1)
        view_toolbar.show()
        self.view_toolbar_button.show()

        button = ToolButton('view-fullscreen')
        button.set_tooltip(_('Fullscreen'))
        button.props.accelerator = '<Alt>Return'
        view_toolbar.insert(button, -1)
        button.show()
        button.connect('clicked', self._fullscreen_cb)

        self._zoom_in = ToolButton('zoom-in')
        self._zoom_in.set_tooltip(_('Increase size'))
        view_toolbar.insert(self._zoom_in, -1)
        self._zoom_in.show()
        self._zoom_in.connect('clicked', self._zoom_in_cb)

        self._zoom_out = ToolButton('zoom-out')
        self._zoom_out.set_tooltip(_('Decrease size'))
        view_toolbar.insert(self._zoom_out, -1)
        self._zoom_out.show()
        self._zoom_out.connect('clicked', self._zoom_out_cb)

        self._zoom_eq = ToolButton('zoom-original')
        self._zoom_eq.set_tooltip(_('Restore original size'))
        view_toolbar.insert(self._zoom_eq, -1)
        self._zoom_eq.show()
        self._zoom_eq.connect('clicked', self._zoom_eq_cb)

        self._set_zoom_buttons_sensitivity()

        edit_toolbar = Gtk.Toolbar()
        self.edit_toolbar_button = ToolbarButton(
            page=edit_toolbar,
            label=_('Edit'),
            icon_name='toolbar-edit')
        self.edit_toolbar_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.edit_toolbar_button, 1)
        edit_toolbar.show()
        self.edit_toolbar_button.show()

        self._copy_button = ToolButton('edit-copy')
        self._copy_button.set_tooltip(_('Copy'))
        self._copy_button.props.accelerator = '<Ctrl>C'
        edit_toolbar.insert(self._copy_button, -1)
        self._copy_button.show()
        self._copy_button.connect('clicked', self._copy_cb)
        self._copy_button.set_sensitive(False)

        self._paste_button = ToolButton('edit-paste')
        self._paste_button.set_tooltip(_('Paste'))
        self._paste_button.props.accelerator = '<Ctrl>V'
        edit_toolbar.insert(self._paste_button, -1)
        self._paste_button.show()
        self._paste_button.connect('clicked', self._paste_cb)
        self._paste_button.set_sensitive(False)

        self._progress_toolbar = Gtk.Toolbar()
        self.progress_toolbar_button = ToolbarButton(
            page=self._progress_toolbar,
            label=_('Check progress'),
            icon_name='check-progress')
        self.progress_toolbar_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.progress_toolbar_button, -1)
        self._progress_toolbar.show()
        self.progress_toolbar_button.show()

        self._help_button = ToolButton('toolbar-help')
        self._help_button.set_tooltip(_('Help'))
        self._help_button.props.accelerator = '<Ctrl>H'
        self._toolbox.toolbar.insert(self._help_button, -1)
        self._help_button.show()
        self._help_button.connect('clicked', self._help_cb)
        self._help_button.set_sensitive(False)
        self._help_button.palette_invoker.props.lock_palette = True

        self.transfer_button = ToolButton('transfer')
        self.transfer_button.set_tooltip(_('Training data upload status'))
        self._toolbox.toolbar.insert(self.transfer_button, -1)
        self.transfer_button.connect('clicked', self._transfer_cb)
        self.transfer_button.hide()

        self.progress_label = Gtk.Label()
        self.progress_label.set_line_wrap(True)
        self.progress_label.set_size_request(300, -1)
        self.progress_label.set_use_markup(True)
        toolitem = Gtk.ToolItem()
        toolitem.add(self.progress_label)
        self.progress_label.show()
        self._toolbox.toolbar.insert(toolitem, -1)
        toolitem.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self._toolbox.toolbar.insert(separator, -1)
        separator.show()

        stop_button = StopButton(self)
        stop_button.props.accelerator = '<Ctrl>q'
        self._toolbox.toolbar.insert(stop_button, -1)
        stop_button.show()

    def _build_progress_toolbar(self):
        self._progress_buttons = []
        progress = self._task_master.get_completed_sections()

        for section_index in range(self._task_master.get_number_of_sections()):
            icon = self._task_master.get_section_icon(section_index)
            if section_index in progress:
                icon = icon + '-white'
            else:
                icon = icon + '-grey'

            name = self._task_master.get_section_name(section_index)

            if section_index == 0:
                group = None
            else:
                group = self._progress_buttons[0]

            self._progress_buttons.append(RadioToolButton(group=group))
            self._progress_buttons[-1].set_icon_name(icon)
            self._progress_buttons[-1].set_tooltip(name)
            self._progress_toolbar.insert(self._progress_buttons[-1], -1)
            self._progress_buttons[-1].show()
            self._progress_buttons[-1].connect(
                'clicked', self._jump_to_section_cb, section_index)

        self._radio_buttons_live = False
        section_index, task_index = \
            self._task_master.get_section_and_task_index()
        self._progress_buttons[section_index].set_active(True)
        self._radio_buttons_live = True

    def _check_connected_task_status(self):
        ''' We only want to turn on notifications if we expect connectivity '''
        task = self._task_master.uid_to_task(GET_CONNECTED_TASK)
        self.set_notify_transfer_status(task.is_completed())

    def _update_completed_sections(self):
        progress = self._task_master.get_completed_sections()

        for section in range(self._task_master.get_number_of_sections()):
            icon = self._task_master.get_section_icon(section)
            if section in progress:
                icon = icon + '-white'
            else:
                icon = icon + '-grey'
            self._progress_buttons[section].set_icon_name(icon)

        self._radio_buttons_live = False
        section_index, task_index = \
            self._task_master.get_section_and_task_index()
        self._progress_buttons[section_index].set_active(True)
        self._radio_buttons_live = True

    def mark_section_as_complete(self, section):
        icon = self._task_master.get_section_icon(section) + '-white'
        self._progress_buttons[section].set_icon_name(icon)
        if section < self._task_master.get_number_of_sections() - 1:
            self._radio_buttons_live = False
            self._progress_buttons[section + 1].set_active(True)
            self._radio_buttons_live = True

    def set_notify_transfer_status(self, state):
        _logger.debug('Setting transfer status to %s' % (str(state)))
        self._notify_transfer_status = state

    def _update_transfer_button(self, icon_name, tooltip):
        self.transfer_button.set_icon_name(icon_name)
        self.transfer_button.set_tooltip(tooltip)
        if self._notify_transfer_status:
            self.transfer_button.show()
        else:
            self.transfer_button.hide()

    def _transfer_cb(self, button):
        ''' Hide the button to dismiss notification '''
        self.transfer_button.set_tooltip(_('Training data upload status'))
        self.transfer_button.hide()

    def __transfer_started_cb(self, widget):
        self._update_transfer_button('transfer', _('Data transfer started'))

    def __transfer_progressed_cb(self, widget):
        self._update_transfer_button('transfer',
                                     _('Data transfer progressing'))

    def __transfer_completed_cb(self, widget):
        self._update_transfer_button('transfer-complete',
                                     _('Data transfer completed'))

    def __transfer_failed_cb(self, widget):
        self._update_transfer_button('transfer-failed',
                                     _('Data transfer failed'))

    def __realize_cb(self, window):
        self.window_xid = window.get_window().get_xid()

    def set_copy_widget(self, webkit=None, text_entry=None):
        # Each task is responsible for setting a widget for copy
        if webkit is not None:
            self._webkit = webkit
        else:
            self._webkit = None
        if text_entry is not None:
            self._copy_entry = text_entry
        else:
            self._copy_entry = None

        self._copy_button.set_sensitive(webkit is not None or
                                        text_entry is not None)

    def _copy_cb(self, button):
        if self._copy_entry is not None:
            self._copy_entry.copy_clipboard()
        elif self._webkit is not None:
            self._webkit.copy_clipboard()
        else:
            _logger.debug('No widget set for copy.')

    def set_paste_widget(self, text_entry=None):
        # Each task is responsible for setting a widget for paste
        if text_entry is not None:
            self._paste_entry = text_entry
        self._paste_button.set_sensitive(text_entry is not None)

    def _paste_cb(self, button):
        clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
        self.clipboard_text = clipboard.wait_for_text()
        if self._paste_entry is not None:
            self._paste_entry.paste_clipboard()
        else:
            _logger.debug('No widget set for paste (%s).' %
                          self.clipboard_text)

    def _fullscreen_cb(self, button):
        ''' Hide the Sugar toolbars. '''
        self.fullscreen()

    def _set_zoom_buttons_sensitivity(self):
        if self.font_size < len(FONT_SIZES) - 1:
            self._zoom_in.set_sensitive(True)
        else:
            self._zoom_in.set_sensitive(False)
        if self.font_size > 0:
            self._zoom_out.set_sensitive(True)
        else:
            self._zoom_out.set_sensitive(False)

        if hasattr(self, '_scrolled_window'):
            self._set_scroll_policy()

    def _set_scroll_policy(self):
        if Gdk.Screen.width() < Gdk.Screen.height() or self.zoom_level > 0.667:
            self._scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
                                             Gtk.PolicyType.AUTOMATIC)
        else:
            self._scrolled_window.set_policy(Gtk.PolicyType.NEVER,
                                             Gtk.PolicyType.AUTOMATIC)

    def _zoom_eq_cb(self, button):
        self.font_size = 8
        self.zoom_level = 0.667
        self._set_zoom_buttons_sensitivity()
        self._task_master.reload_graphics()

    def _zoom_in_cb(self, button):
        if self.font_size < len(FONT_SIZES) - 1:
            self.font_size += 1
            self.zoom_level *= 1.1
        self._set_zoom_buttons_sensitivity()
        self._task_master.reload_graphics()

    def _zoom_out_cb(self, button):
        if self.font_size > 0:
            self.font_size -= 1
            self.zoom_level /= 1.1
        self._set_zoom_buttons_sensitivity()
        self._task_master.reload_graphics()

    def _jump_to_section_cb(self, button, section_index):
        if self._radio_buttons_live:
            if self._task_master.requirements_are_met(section_index, 0):
                uid = self._task_master.section_and_task_to_uid(section_index)
                self._task_master.current_task = \
                    self._task_master.uid_to_task_number(uid)
                self._task_master.reload_graphics()
            else:
                section_index, task_index = \
                    self._task_master.get_section_and_task_index()
                self._progress_buttons[section_index].set_active(True)

    def _help_cb(self, button):
        # title, help_file = self._task_master.get_help_info()
        # _logger.debug('%s: %s' % (title, help_file))
        # if not hasattr(self, 'window_xid'):
        #    self.window_xid = self.get_window().get_xid()
        # if title is not None and help_file is not None:
        #     self.viewhelp = ViewHelp(title, help_file, self.window_xid)
        #     self.viewhelp.show()
        try:
            self._help_panel.set_connected(
                utils.nm_status() == 'network-wireless-connected')
        except Exception as e:
            _logger.error('Could not read NM status: %s' % (e))
            self._help_panel.set_connected(False)

        if self.help_palette:
            # FIXME: is_up() is always returning False, so we
            # "debounce" using help_panel_visible.
            if not self.help_palette.is_up() and not self.help_panel_visible:
                self.help_palette.popup(
                    immediate=True, state=self.help_palette.SECONDARY)
                self.help_panel_visible = True
            else:
                self.help_palette.popdown(immediate=True)
                self.help_panel_visible = False
                self._help_button.set_expanded(False)

    def add_badge(self, msg, icon="training-trophy", name="One Academy"):
        sugar_icons = os.path.join(os.path.expanduser('~'), '.icons')
        if not os.path.exists(sugar_icons):
            try:
                subprocess.call(['mkdir', sugar_icons])
            except OSError as e:
                _logger.error('Could not mkdir %s, %s' % (sugar_icons, e))

        badge = {
            'icon': icon,
            'from': name,
            'message': msg
        }

        # Use icons from html-content directory since default colors are
        # intended for white background.
        icon_dir = os.path.join(self.bundle_path, 'html-content', 'images')
        icon_path = os.path.join(icon_dir, icon + '.svg')
        try:
            subprocess.call(['cp', icon_path, sugar_icons])
        except OSError as e:
            _logger.error('Could not copy %s to %s, %s' %
                          (icon_path, sugar_icons, e))

        if 'comments' in self.metadata:
            comments = json.loads(self.metadata['comments'])
            comments.append(badge)
            self.metadata['comments'] = json.dumps(comments)
        else:
            self.metadata['comments'] = json.dumps([badge])

    def _load_extension(self):
        if not WEBSERVICES_AVAILABLE:
            _logger.error('Webservices not available on this version of Sugar')
            self._webservice_alert(_('Sugar upgrade required.'))
            return False

        extensions_path = os.path.join(os.path.expanduser('~'), '.sugar',
                                       'default', 'extensions')
        webservice_path = os.path.join(extensions_path, 'webservice')
        sugarservices_path = os.path.join(self.bundle_path, 'sugarservices')
        init_path = os.path.join(self.bundle_path, 'sugarservices',
                                 '__init__.py')

        if not os.path.exists(extensions_path):
            try:
                subprocess.call(['mkdir', extensions_path])
            except OSError as e:
                _logger.error('Could not mkdir %s, %s' % (extensions_path, e))
                self._webservice_alert(_('System error.'))
                return False

        if not os.path.exists(webservice_path):
            try:
                subprocess.call(['mkdir', webservice_path])
            except OSError as e:
                _logger.error('Could not mkdir %s, %s' % (webservice_path, e))
                self._webservice_alert(_('System error.'))
                return False
            try:
                subprocess.call(['cp', init_path, webservice_path])
            except OSError as e:
                _logger.error('Could not cp %s to %s, %s' %
                              (init_path, webservice_path, e))
                self._webservice_alert(_('System error.'))
                return False

        install = False
        if not os.path.exists(os.path.join(webservice_path, 'sugarservices')):
            _logger.error('SugarServices webservice not found. Installing...')
            install = True
        elif utils.get_sugarservices_version() < \
             _REQUIRED_SUGARSERVICES_VERSION:
            _logger.error('Found old SugarServices version. Installing...')
            install = True

        if install:
            try:
                subprocess.call(['cp', '-r', sugarservices_path,
                                 webservice_path])
            except OSError as e:
                _logger.error('Could not copy %s to %s, %s' %
                              (sugarservices_path, webservice_path, e))
                self._webservice_alert(_('System error.'))
                return False

            alert = ConfirmationAlert()
            alert.props.title = _('Restart required')
            alert.props.msg = _('We needed to install some software on your '
                                'system.\nSugar must be restarted before '
                                'sugarservices can commence.')

            alert.connect('response', self._reboot_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(file_name='restart.html')

        return not install

    def _webservice_alert(self, message):
        alert = ConfirmationAlert()
        alert.props.title = message
        alert.props.msg = _('We are unable to install some software on your '
                            'system.\nSugar must be upgraded before this '
                            'activity can be run.')

        alert.connect('response', self._close_alert_cb)
        self.add_alert(alert)
        self._load_intro_graphics(message=message)

    def _remove_alert_cb(self, alert, response_id):
        self.remove_alert(alert)

    def _close_alert_cb(self, alert, response_id):
        self.remove_alert(alert)
        if response_id is Gtk.ResponseType.OK:
            self.close()

    def _reboot_alert_cb(self, alert, response_id):
        self.remove_alert(alert)
        if response_id is Gtk.ResponseType.OK:
            try:
                utils.reboot()
            except Exception as e:
                _logger.error('Cannot reboot: %s' % e)

    def _mount_added_cb(self, volume_monitor, device):
        _logger.error('mount added')
        if self.check_volume_data():
            _logger.debug('launching')
            self._launcher()

    def _mount_removed_cb(self, volume_monitor, device):
        _logger.error('mount removed')
        if self.check_volume_data():
            _logger.debug('launching')
            self._launcher()
Ejemplo n.º 15
0
class PhysicsActivity(activity.Activity):
    def __init__(self, handle):
        super(PhysicsActivity, self).__init__(handle)
        self._collab = CollabWrapper(self)
        self._collab.message.connect(self.__message_cb)
        self.metadata["mime_type"] = "application/x-physics-activity"
        self.add_events(Gdk.EventMask.ALL_EVENTS_MASK | Gdk.EventMask.VISIBILITY_NOTIFY_MASK)

        self.connect("visibility-notify-event", self._focus_event)
        self.connect("window-state-event", self._window_event)

        self.game_canvas = sugargame.canvas.PygameCanvas(self)
        self.game = physics.main(self)

        self.preview = None
        self._sample_window = None

        self._fixed = Gtk.Fixed()
        self._fixed.put(self.game_canvas, 0, 0)

        w = Gdk.Screen.width()
        h = Gdk.Screen.height() - 2 * GRID_CELL_SIZE

        self.game_canvas.set_size_request(w, h)

        self._constructors = {}
        self.build_toolbar()

        self.set_canvas(self._fixed)
        Gdk.Screen.get_default().connect("size-changed", self.__configure_cb)

        logging.debug(os.path.join(activity.get_activity_root(), "data", "data"))
        self.game_canvas.run_pygame(self.game.run)
        self.show_all()
        self._collab.setup()

    def __configure_cb(self, event):
        """ Screen size has changed """
        self.write_file(os.path.join(activity.get_activity_root(), "data", "data"))
        w = Gdk.Screen.width()
        h = Gdk.Screen.height() - 2 * GRID_CELL_SIZE
        pygame.display.set_mode((w, h), pygame.RESIZABLE)
        self.read_file(os.path.join(activity.get_activity_root(), "data", "data"))
        self.game.run(True)

    def read_file(self, file_path):
        self.game.read_file(file_path)

    def write_file(self, file_path):
        self.game.write_file(file_path)

    def get_preview(self):
        """ Custom preview code to get image from pygame. """
        return self.game_canvas.get_preview()

    def build_toolbar(self):
        self.max_participants = 4

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        create_toolbar = ToolbarButton()
        create_toolbar.props.page = Gtk.Toolbar()
        create_toolbar.props.icon_name = "magicpen"
        create_toolbar.props.label = _("Create")
        toolbar_box.toolbar.insert(create_toolbar, -1)
        self._insert_create_tools(create_toolbar)

        color = ColorToolButton("Color Button")
        color.props.icon_name = "color"
        color.connect("notify::color", self.returnChosenColor)
        toolbar_box.toolbar.insert(color, -1)
        color.show()

        self.randomColor = ToggleToolButton("Random Color")
        self.randomColor.set_tooltip(_("Toggle random color"))
        self.randomColor.props.icon_name = "colorRandom"
        self.randomColor.connect("toggled", self.resetColors)
        toolbar_box.toolbar.insert(self.randomColor, -1)
        self.randomColor.set_active(True)
        self.randomColor.show()

        self._insert_stop_play_button(toolbar_box.toolbar)

        clear_trace = ToolButton("clear-trace")
        clear_trace.set_tooltip(_("Clear Trace Marks"))
        clear_trace.set_accelerator(_("<ctrl>x"))
        clear_trace.connect("clicked", self.clear_trace_cb)
        clear_trace.set_sensitive(False)
        toolbar_box.toolbar.insert(clear_trace, -1)
        clear_trace.show()
        self.clear_trace = clear_trace

        self._insert_clear_all_button(toolbar_box.toolbar)

        load_example = ToolButton("load-sample")
        load_example.set_tooltip(_("Show sample projects"))
        load_example.connect("clicked", self._create_store)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        toolbar_box.toolbar.insert(load_example, -1)
        load_example.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_size_request(0, -1)
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop = StopButton(self)
        toolbar_box.toolbar.insert(stop, -1)
        stop.show()

        separator = Gtk.SeparatorToolItem()
        activity_button.props.page.insert(separator, -1)
        separator.show()

        export_json = ToolButton("save-as-json")
        export_json.set_tooltip(_("Export tracked objects to journal"))
        export_json.connect("clicked", self._export_json_cb)
        activity_button.props.page.insert(export_json, -1)
        export_json.show()

        load_project = ToolButton("load-project")
        load_project.set_tooltip(_("Load project from journal"))
        load_project.connect("clicked", self._load_project)
        activity_button.props.page.insert(load_project, -1)
        load_project.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()
        create_toolbar.set_expanded(True)
        return toolbar_box

    def returnChosenColor(self, widget, pspec):
        """
        input: used as a connect function for Gtk Color widget The
        conversion that follows is required because Gtk Color is
        stored in RGB with the highest value being 65535, whereas this
        program stores color with the highest value being 255.
        """
        color = widget.get_color()
        red = (color.red / 65535.0) * 255
        green = (color.green / 65535.0) * 255
        blue = (color.blue / 65535.0) * 255
        objectColor = ((red), (green), (blue))
        self.randomColor.set_active(False)

        self.game.world.add.setColor(objectColor)

    def resetColors(self, button):
        self.game.world.add.resetColor()

    def can_close(self):
        self.preview = self.get_preview()
        self.game.loop = False
        return True

    def _insert_stop_play_button(self, toolbar):

        self.stop_play_toolbar = ToolbarButton()
        st_toolbar = self.stop_play_toolbar
        st_toolbar.props.page = Gtk.Toolbar()
        st_toolbar.props.icon_name = "media-playback-stop"

        self.stop_play_state = True
        self.stop_play = ToolButton("media-playback-stop")
        self.stop_play.set_tooltip(_("Stop"))
        self.stop_play.set_accelerator(_("<ctrl>space"))
        self.stop_play.connect("clicked", self.stop_play_cb)
        self._insert_item(st_toolbar, self.stop_play)
        self.stop_play.show()

        slowest_button = RadioToolButton(group=None)
        slowest_button.set_icon_name("slow-walk-milton-raposo")
        slowest_button.set_tooltip(_("Run slower"))
        slowest_button.connect("clicked", self._set_fps_cb, SLOWEST_FPS)
        self._insert_item(st_toolbar, slowest_button)
        slowest_button.show()

        slow_button = RadioToolButton(group=slowest_button)
        slow_button.set_icon_name("walking")
        slow_button.set_tooltip(_("Run slow"))
        slow_button.connect("clicked", self._set_fps_cb, SLOW_FPS)
        self._insert_item(st_toolbar, slow_button)
        slow_button.show()

        fast_button = RadioToolButton(group=slowest_button)
        fast_button.set_icon_name("running")
        fast_button.set_tooltip("Run fast")
        fast_button.connect("clicked", self._set_fps_cb, FAST_FPS)
        self._insert_item(st_toolbar, fast_button)
        fast_button.show()
        fast_button.set_active(True)

        toolbar.insert(self.stop_play_toolbar, -1)
        self.stop_play_toolbar.show_all()

    def _set_fps_cb(self, button, value):
        self.game.set_game_fps(value)

    def _insert_clear_all_button(self, toolbar):
        self.clear_all = ToolButton("clear_all")
        self.clear_all.set_tooltip(_("Erase All"))
        self.clear_all.set_accelerator(_("<ctrl>a"))
        self.clear_all.connect("clicked", self.clear_all_cb)
        toolbar.insert(self.clear_all, -1)
        self.clear_all.set_sensitive(False)
        self.clear_all.show()

    def _insert_item(self, toolbar, item, pos=-1):
        if hasattr(toolbar, "insert"):
            toolbar.insert(item, pos)
        else:
            toolbar.props.page.insert(item, pos)

    def _insert_create_tools(self, create_toolbar):
        # Make + add the component buttons
        self.radioList = {}
        for i, c in enumerate(tools.allTools):
            if i == 0:
                button = RadioToolButton(group=None)
                firstbutton = button
            else:
                button = RadioToolButton(group=firstbutton)
            button.set_icon_name(c.icon)
            button.set_tooltip(c.toolTip)
            button.set_accelerator(c.toolAccelerator)
            button.connect("clicked", self.radioClicked)
            palette = self._build_palette(c)
            if palette is not None:
                palette.show()
                button.get_palette().set_content(palette)
            self._insert_item(create_toolbar, button, -1)
            button.show()
            self.radioList[button] = c.name
            if hasattr(c, "constructor"):
                self._constructors[c.name] = self.game.toolList[c.name].constructor

    def __icon_path(self, name):
        activity_path = activity.get_bundle_path()
        icon_path = os.path.join(activity_path, "icons", name + ".svg")
        return icon_path

    def _build_palette(self, tool):
        if tool.palette_enabled:
            if tool.palette_mode == tools.PALETTE_MODE_ICONS:
                grid = Gtk.Grid()
                for s, settings in enumerate(tool.palette_settings):
                    self.game.toolList[tool.name].buttons.append([])
                    for i, icon_value in enumerate(settings["icon_values"]):
                        if i == 0:
                            button = RadioToolButton(group=None)
                            firstbutton = button
                        else:
                            button = RadioToolButton(group=firstbutton)
                        button.set_icon_name(settings["icons"][i])
                        button.connect(
                            "clicked", self._palette_icon_clicked, tool.name, s, settings["name"], icon_value
                        )
                        grid.attach(button, i, s, 1, 1)
                        self.game.toolList[tool.name].buttons[s].append(button)
                        button.show()
                        if settings["active"] == settings["icons"][i]:
                            button.set_icon_name(settings["icons"][i] + "-selected")
                            button.set_active(True)
                return grid
        else:
            return None

    def _palette_icon_clicked(self, widget, toolname, s, value_name, value):
        for tool in tools.allTools:
            if tool.name == toolname:
                # Radio buttons are not highlighting in the palette
                # so adding highlight by hand
                # See http://bugs.sugarlabs.org/ticket/305
                setting = self.game.toolList[tool.name].palette_settings[s]
                for i, button in enumerate(self.game.toolList[tool.name].buttons[s]):
                    icon_name = setting["icons"][i]
                    if button == widget:
                        button.set_icon_name(icon_name + "-selected")
                    else:
                        button.set_icon_name(icon_name)
                if hasattr(tool, "palette_data_type"):
                    tool.palette_data_type = value
                else:
                    tool.palette_data[value_name] = value

    def clear_trace_alert_cb(self, alert, response):
        self.remove_alert(alert)
        if response is Gtk.ResponseType.OK:
            self.game.full_pos_list = [[] for _ in self.game.full_pos_list]
            self.game.tracked_bodies = 0

    def clear_trace_cb(self, button):
        clear_trace_alert = ConfirmationAlert()
        clear_trace_alert.props.title = _("Are You Sure?")
        clear_trace_alert.props.msg = _("All trace points will be erased. This cannot be undone!")
        clear_trace_alert.connect("response", self.clear_trace_alert_cb)
        self.add_alert(clear_trace_alert)

    def stop_play_cb(self, button):
        pygame.event.post(pygame.event.Event(pygame.USEREVENT, action="stop_start_toggle"))
        self.stop_play_state = not self.stop_play_state

        if self.stop_play_state:
            self.stop_play.set_icon_name("media-playback-stop")
            self.stop_play.set_tooltip(_("Stop"))

            self.stop_play_toolbar.set_icon_name("media-playback-stop")
        else:
            self.stop_play.set_icon_name("media-playback-start")
            self.stop_play.set_tooltip(_("Start"))

            self.stop_play_toolbar.set_icon_name("media-playback-start")

    def clear_all_cb(self, button):
        def clear_all_alert_cb(alert, response_id):
            self.remove_alert(alert)
            if response_id is Gtk.ResponseType.OK:
                pygame.event.post(pygame.event.Event(pygame.USEREVENT, action="clear_all"))

        if len(self.game.world.world.bodies) > 2:
            clear_all_alert = ConfirmationAlert()
            clear_all_alert.props.title = _("Are You Sure?")
            clear_all_alert.props.msg = _("All your work will be discarded. This cannot be undone!")
            clear_all_alert.connect("response", clear_all_alert_cb)
            self.add_alert(clear_all_alert)

    def radioClicked(self, button):
        pygame.event.post(pygame.event.Event(pygame.USEREVENT, action=self.radioList[button]))

    def _focus_event(self, event, data=None):
        """ Send focus events to pygame to allow it to idle when in
        background. """
        if not self.game.pygame_started:
            logging.debug("focus_event: pygame not yet initialized")
            return
        if data.state == Gdk.VisibilityState.FULLY_OBSCURED:
            pygame.event.post(pygame.event.Event(pygame.USEREVENT, action="focus_out"))
        else:
            self.game.show_fake_cursor = True
            pygame.event.post(pygame.event.Event(pygame.USEREVENT, action="focus_in"))

    def _export_json_cb(self, button):
        jobject = datastore.create()
        jobject.metadata["title"] = _("Physics export")
        jobject.metadata["mime_type"] = "text/plain"

        tmp_dir = os.path.join(self.get_activity_root(), "instance")
        fd, file_path = tempfile.mkstemp(dir=tmp_dir)
        os.close(fd)

        self.game.world.json_save(file_path)

        jobject.set_file_path(file_path)
        datastore.write(jobject)

    def _window_event(self, window, event):
        """ Send focus out event to pygame when switching to a desktop
        view. """
        if event.changed_mask & Gdk.WindowState.ICONIFIED:
            pygame.event.post(pygame.event.Event(pygame.USEREVENT, action="focus_out"))

    def _restore_cursor(self):
        """ No longer waiting, so restore standard cursor. """
        if not hasattr(self, "get_window"):
            return
        self.get_window().set_cursor(self.old_cursor)

    def _waiting_cursor(self):
        """ Waiting, so set watch cursor. """
        if not hasattr(self, "get_window"):
            return
        self.old_cursor = self.get_window().get_cursor()
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))

    def __message_cb(self, collab, buddy, msg):
        """ Data is passed as tuples: cmd:text """
        action = msg.get("action")
        if action != "text":
            return

        text = msg["text"]
        dispatch_table = {
            "C": self._construct_shared_circle,
            "B": self._construct_shared_box,
            "T": self._construct_shared_triangle,
            "P": self._construct_shared_polygon,
            "M": self._construct_shared_magicpen,
            "j": self._add_shared_joint,
            "p": self._add_shared_pin,
            "m": self._add_shared_motor,
            "t": self._add_shared_track,
            "c": self._add_shared_chain,
        }
        logging.debug("<<< %s" % (text[0]))
        dispatch_table[text[0]](text[2:])

    def _construct_shared_circle(self, data):
        circle_data = json.loads(data)
        pos = circle_data[0]
        radius = circle_data[1]
        density = circle_data[2]
        restitution = circle_data[3]
        friction = circle_data[4]
        self._constructors["Circle"](pos, radius, density, restitution, friction, share=False)

    def _construct_shared_box(self, data):
        box_data = json.loads(data)
        pos1 = box_data[0]
        pos2 = box_data[1]
        density = box_data[2]
        restitution = box_data[3]
        friction = box_data[4]
        self._constructors["Box"](pos1, pos2, density, restitution, friction, share=False)

    def _construct_shared_triangle(self, data):
        triangle_data = json.loads(data)
        pos1 = triangle_data[0]
        pos2 = triangle_data[1]
        density = triangle_data[2]
        restitution = triangle_data[3]
        friction = triangle_data[4]
        self._constructors["Triangle"](pos1, pos2, density, restitution, friction, share=False)

    def _construct_shared_polygon(self, data):
        polygon_data = json.loads(data)
        vertices = polygon_data[0]
        density = polygon_data[1]
        restitution = polygon_data[2]
        friction = polygon_data[3]
        self._constructors["Polygon"](vertices, density, restitution, friction, share=False)

    def _construct_shared_magicpen(self, data):
        magicpen_data = json.loads(data)
        vertices = magicpen_data[0]
        density = magicpen_data[1]
        restitution = magicpen_data[2]
        friction = magicpen_data[3]
        self._constructors["Magicpen"](vertices, density, restitution, friction, share=False)

    def _add_shared_joint(self, data):
        joint_data = json.loads(data)
        pos1 = joint_data[0]
        pos2 = joint_data[1]
        self._constructors["Joint"](pos1, pos2, share=False)

    def _add_shared_pin(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        self._constructors["Pin"](pos, share=False)

    def _add_shared_motor(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        speed = joint_data[1]
        self._constructors["Motor"](pos, speed, share=False)

    def _add_shared_track(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        color = joint_data[1]
        self._constructors["Track"](pos, color, share=False)

    def _add_shared_chain(self, data):
        joint_data = json.loads(data)
        vertices = joint_data[0]
        link_length = joint_data[1]
        radius = joint_data[2]
        self._constructors["Chain"](vertices, link_length, radius, share=False)

    def send_event(self, text):
        self._collab.post(dict(action="text", text=text))

    def _load_project(self, button):
        chooser = ObjectChooser(parent=self)
        result = chooser.run()
        if result == Gtk.ResponseType.ACCEPT:
            dsobject = chooser.get_selected_object()
            file_path = dsobject.get_file_path()
            self.__load_game(file_path, True)
            chooser.destroy()

    def __load_game(self, file_path, journal=False):
        confirmation_alert = ConfirmationAlert()
        confirmation_alert.props.title = _("Are You Sure?")
        confirmation_alert.props.msg = _("All your work will be discarded. This cannot be undone!")

        def action(alert, response):
            self.remove_alert(alert)
            if response is not Gtk.ResponseType.OK:
                return

            try:
                f = open(file_path, "r")
                # Test if the file is valid project.
                json.loads(f.read())
                f.close()

                self.read_file(file_path)
                self.game.run(True)
            except:
                title = _("Load project from journal")
                if not journal:
                    title = _("Load example")
                msg = _("Error: Cannot open Physics project from this file.")
                alert = NotifyAlert(5)
                alert.props.title = title
                alert.props.msg = msg
                alert.connect("response", lambda alert, response: self.remove_alert(alert))
                self.add_alert(alert)

        confirmation_alert.connect("response", action)
        self.add_alert(confirmation_alert)

    def _create_store(self, widget=None):
        width = Gdk.Screen.width() / 4
        height = Gdk.Screen.height() / 4

        if self._sample_window is None:
            self._sample_box = Gtk.EventBox()

            vbox = Gtk.VBox()
            self._sample_window = Gtk.ScrolledWindow()
            self._sample_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
            self._sample_window.set_border_width(4)
            w = Gdk.Screen.width() / 2
            h = Gdk.Screen.height() / 2
            self._sample_window.set_size_request(w, h)
            self._sample_window.show()

            store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)

            icon_view = Gtk.IconView()
            icon_view.set_model(store)
            icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE)
            icon_view.connect("selection-changed", self._sample_selected, store)
            icon_view.set_pixbuf_column(0)
            icon_view.grab_focus()
            self._sample_window.add(icon_view)
            icon_view.show()
            self._fill_samples_list(store)

            title = Gtk.HBox()
            title_label = Gtk.Label(_("Select a sample..."))
            separator = Gtk.HSeparator()
            separator.props.expand = True
            separator.props.visible = False

            btn = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL)
            btn.connect("clicked", lambda button: self._sample_box.hide())

            title.pack_start(title_label, False, False, 5)
            title.pack_start(separator, False, False, 0)
            title.pack_end(btn, False, False, 0)

            vbox.pack_start(title, False, False, 5)
            vbox.pack_end(self._sample_window, True, True, 0)

            self._sample_box.add(vbox)

            self._fixed.put(self._sample_box, width, height)

        if self._sample_window:
            # Remove and add again. Maybe its on portrait mode.
            self._fixed.remove(self._sample_box)
            self._fixed.put(self._sample_box, width, height)

        self._sample_box.show_all()

    def _get_selected_path(self, widget, store):
        try:
            iter_ = store.get_iter(widget.get_selected_items()[0])
            image_path = store.get(iter_, 1)[0]

            return image_path, iter_
        except:
            return None

    def _sample_selected(self, widget, store):
        selected = self._get_selected_path(widget, store)

        if selected is None:
            self._selected_sample = None
            self._sample_box.hide()
            return

        image_path, _iter = selected
        iter_ = store.get_iter(widget.get_selected_items()[0])
        image_path = store.get(iter_, 1)[0]

        self._selected_sample = image_path
        self._sample_box.hide()

        self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH))
        GObject.idle_add(self._sample_loader)

    def _sample_loader(self):
        # Convert from thumbnail path to sample path
        basename = os.path.basename(self._selected_sample)[:-4]
        file_path = os.path.join(activity.get_bundle_path(), "samples", basename + ".json")
        self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR))
        self.__load_game(file_path)

    def _fill_samples_list(self, store):
        """
        Append images from the artwork_paths to the store.
        """
        for filepath in self._scan_for_samples():
            pixbuf = None
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filepath, 100, 100)
            store.append([pixbuf, filepath])

    def _scan_for_samples(self):
        samples = sorted(glob.glob(os.path.join(activity.get_bundle_path(), "samples", "thumbnails", "*.png")))
        return samples
Ejemplo n.º 16
0
class SideToolbar(Gtk.Toolbar):
    ''' A toolbar on the side of the canvas for adjusting gain/bias '''

    def __init__(self, activity, channel=0):
        ''' Set up initial toolbars '''
        super(type(self), self).__init__()

        self.activity = activity
        self._channel = channel
        if self._channel == 0:
            self.show_toolbar = True
        else:  # Don't show second channel until we need it
            self.show_toolbar = False
        self.mode = 'sound'
        self.mode_values = {'sound': 3, 'sensor': 2}

        self._toggle = Gtk.CheckButton()
        self._toggle.set_active(True)
        self._toggle.connect('clicked', self.toggle_cb)
        self._toggle.show()
        self._toggle_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self._toggle_box.pack_start(self._toggle, False, True, 18)

        self._invert = ToolButton('invert')
        self._invert.set_tooltip(_('Invert'))
        self._invert.connect('clicked', self.invert_control_cb)
        self._invert.show()
        self.activity.wave.set_invert_state(False, channel=self._channel)

        self.button_up = ToolButton('amp-high')
        self.button_up.set_tooltip(_('Increase amplitude'))
        self.button_up.connect('clicked', self._button_up_cb)
        self.button_up.show()

        self.adjustmenty = Gtk.Adjustment(self.mode_values[self.mode],
                                          LOWER, UPPER,
                                          0.1, 0.1, 0.0)
        self.adjustmenty.connect('value_changed', self._yscrollbar_cb,
                                 self.adjustmenty)
        self.yscrollbar = Gtk.Scale(orientation=Gtk.Orientation.VERTICAL,
                                    adjustment=self.adjustmenty)
        self.yscrollbar.set_draw_value(False)
        self.yscrollbar.set_inverted(True)

        self.button_down = ToolButton('amp-low')
        self.button_down.set_tooltip(_('Decrease amplitude'))
        self.button_down.connect('clicked', self._button_down_cb)
        self.button_down.show()

        self.box1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
                            homogeneous=False, spacing=0)

        if self._channel == 0:
            self.box1.pack_start(self._color_wave(self.activity.stroke_color),
                                 False, True, 0)
        elif self._channel == 1:
            self.box1.pack_start(self._color_wave(self.activity.fill_color),
                                 False, True, 0)
        else:
            self.box1.pack_start(self._color_wave('#FFFFFF'), False, True, 0)
        self.box1.pack_start(self._toggle_box, False, True, 0)
        self.box1.pack_start(self._invert, False, True, 0)
        self.box1.pack_start(self.button_up, False, True, 0)
        self.box1.pack_start(self.yscrollbar, True, True, 0)
        self.box1.pack_start(self.button_down, False, True, 0)

        self.set_show_hide(False)

    def _yscrollbar_cb(self, adjy, data=None):
        ''' Callback for scrollbar '''
        if self.mode == 'sound':
            self.activity.wave.set_mag_params(1.0, adjy.get_value(),
                                              channel=self._channel)
            self.activity.wave.set_bias_param(0,
                                              channel=self._channel)
        elif self.mode == 'sensor':
            self.activity.wave.set_bias_param(int(
                300 * (adjy.get_value() - (UPPER - LOWER) / 2.)),
                channel=self._channel)
        self.mode_values[self.mode] = adjy.get_value()
        return True

    def _button_up_cb(self, data=None):
        '''Moves slider up'''
        new_value = self.yscrollbar.get_value() + (UPPER - LOWER) \
            / 100.
        if new_value <= UPPER:
            self.yscrollbar.set_value(new_value)
        else:
            self.yscrollbar.set_value(UPPER)
        return True

    def _button_down_cb(self, data=None):
        '''Moves slider down'''
        new_value = self.yscrollbar.get_value() - (UPPER - LOWER) \
            / 100.
        if new_value >= LOWER:
            self.yscrollbar.set_value(new_value)
        else:
            self.yscrollbar.set_value(LOWER)
        return True

    def set_show_hide(self, show=True, mode='sound'):
        ''' Show or hide the toolbar '''
        self.show_toolbar = show
        self.set_side_mode(mode)

    def set_side_mode(self, mode='sound'):
        ''' Set the toolbar to either 'sound' or 'sensor' '''
        self.mode = mode
        if self.mode == 'sound':
            self.button_up.set_icon_name('amp-high')
            self.button_up.set_tooltip(_('Increase amplitude'))
            self.button_down.set_icon_name('amp-low')
            self.button_down.set_tooltip(_('Decrease amplitude'))
        elif self.mode == 'sensor':
            self.button_up.set_icon_name('bias-high')
            self.button_up.set_tooltip(_('Increase bias'))
            self.button_down.set_icon_name('bias-low')
            self.button_down.set_tooltip(_('Decrease bias'))
            self._invert.show()
        self.yscrollbar.set_value(self.mode_values[self.mode])
        return

    def toggle_cb(self, data=None):
        self.activity.wave.set_visibility(self._toggle.get_active(),
                                          channel=self._channel)
        return True

    def invert_control_cb(self, data=None):
        ''' Callback for Invert Button '''
        if self.activity.wave.get_invert_state(channel=self._channel):
            self.activity.wave.set_invert_state(False, self._channel)
            self._invert.set_icon_name('invert')
            self._invert.show()
        elif not self.activity.wave.get_fft_mode():
            self.activity.wave.set_invert_state(True, self._channel)
            self._invert.set_icon_name('invert2')
            self._invert.show()
        self.activity.sensor_toolbar.update_string_for_textbox()
        return False

    def _color_wave(self, color):
        ''' Generate a wave graphic in color to label each side toolbar '''
        svg = '<?xml version="1.0" ?>\n\
<!DOCTYPE svg  PUBLIC "-//W3C//DTD SVG 1.1//EN"\n\
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n\
<svg enable-background="new 0 0 55.125 55" height="55px" version="1.1"\n\
viewBox="0 0 55.125 55" width="55.125px" x="0px" xml:space="preserve"\n\
xmlns="http://www.w3.org/2000/svg"\n\
xmlns:xlink="http://www.w3.org/1999/xlink" y="0px">\n\
<path d="M9.066,27.5 c2.32-6.917,4.666-13.834,9.255-13.834\n\
c9.179,0,9.179,27.668,18.358,27.668c4.59,0,6.986-6.917,9.332-13.834"\n\
fill="none" stroke="%s" stroke-linecap="round" stroke-width="3.5"/>\n\
</svg>' % (color)
        pixbuf = svg_str_to_pixbuf(svg)
        img = Gtk.Image()
        img.set_from_pixbuf(pixbuf)
        img_tool = Gtk.ToolItem()
        img_tool.add(img)
        return img_tool
Ejemplo n.º 17
0
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self.set_title('FotoToon')

        self._max_participants = 1
        self.page = None

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        activity_toolbar = activity_button.page
        toolbar_box.toolbar.insert(activity_button, 0)

        edit_toolbar_btn = ToolbarButton()
        edit_toolbar = Gtk.Toolbar()
        edit_toolbar_btn.props.page = edit_toolbar
        edit_toolbar_btn.props.icon_name = 'toolbar-edit'
        edit_toolbar_btn.label = _('Edit')
        toolbar_box.toolbar.insert(edit_toolbar_btn, -1)

        view_toolbar_btn = ToolbarButton()
        view_toolbar = Gtk.Toolbar()
        view_toolbar_btn.props.page = view_toolbar
        view_toolbar_btn.props.icon_name = 'toolbar-view'
        view_toolbar_btn.label = _('View')
        toolbar_box.toolbar.insert(view_toolbar_btn, -1)

        slideview_btn = ToggleToolButton('slideshow')
        slideview_btn.set_tooltip(_('Slideshow'))
        slideview_btn.set_active(False)
        slideview_btn.connect('clicked', self._switch_view_mode, False)
        view_toolbar.insert(slideview_btn, -1)
        slideview_btn.show()

        slideview_timings_btn = ToggleToolButton('slideshow-stopwatch')
        slideview_timings_btn.set_tooltip(_('Slideshow with Timings'))
        slideview_timings_btn.set_active(False)
        slideview_timings_btn.connect('clicked', self._switch_view_mode, True)
        view_toolbar.insert(slideview_timings_btn, -1)
        slideview_timings_btn.show()

        time_button = ToolButton('stopwatch')
        time_button.set_tooltip(_('Set Image Duration in Slideshow (Seconds)'))
        view_toolbar.insert(time_button, -1)
        time_button.show()

        self._time_spin = Gtk.SpinButton.new_with_range(MIN_TIME, MAX_TIME, 1)
        self._time_spin.connect('value-changed', self.__time_spin_changed_cb)
        self._time_spin.props.value = DEFAULT_TIME
        self._time_spin.props.update_policy = \
            Gtk.SpinButtonUpdatePolicy.IF_VALID

        palette = time_button.get_palette()
        palette.connect('popup', self.__time_button_popup_cb)
        time_button.connect(
            'clicked', lambda *args:
            palette.popup(immediate=True, state=Palette.SECONDARY))

        alignment = Gtk.Alignment()
        alignment.set_padding(style.DEFAULT_PADDING, style.DEFAULT_PADDING,
                              style.DEFAULT_PADDING, style.DEFAULT_PADDING)
        alignment.add(self._time_spin)
        self._time_spin.show()
        palette.set_content(alignment)
        alignment.show()

        fullscreen_btn = ToolButton('view-fullscreen')
        fullscreen_btn.set_tooltip(_('Fullscreen'))
        fullscreen_btn.props.accelerator = '<Alt>Return'
        fullscreen_btn.connect('clicked', lambda w: self.fullscreen())
        view_toolbar.insert(fullscreen_btn, -1)
        fullscreen_btn.show()

        self.set_toolbar_box(toolbar_box)

        toolbar = toolbar_box.toolbar

        self.page = Page()

        self.globes_manager = GlobesManager(toolbar, edit_toolbar, self)

        # fonts
        self._text_button = ToolbarButton()
        self._text_button.props.page = TextToolbar(self.page)
        self._text_button.props.icon_name = 'format-text-size'
        self._text_button.props.label = _('Text')
        self._toolbar_box.toolbar.insert(self._text_button, -1)

        reorder_img_btn = ToolButton('thumbs-view')
        reorder_img_btn.set_icon_name('thumbs-view')
        reorder_img_btn.set_tooltip(_('Change image order'))
        reorder_img_btn.connect('clicked', self.__image_order_cb)
        edit_toolbar.insert(reorder_img_btn, -1)
        reorder_img_btn.show()

        bgchange = ToolButton(icon_name='contract-coordinates')
        bgchange.set_tooltip(_('Edit background image'))
        bgchange.connect('clicked', self.__bgchange_clicked_cb)
        edit_toolbar.insert(bgchange, -1)
        bgchange.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)

        toolbar_box.toolbar.insert(separator, -1)

        stop = StopButton(self)
        toolbar_box.toolbar.insert(stop, -1)

        toolbar_box.show_all()

        # add export button

        separator_2 = Gtk.SeparatorToolItem()
        separator_2.show()
        activity_toolbar.insert(separator_2, -1)

        self.bt_save_as_image = ToolButton()
        self.bt_save_as_image.props.icon_name = 'save-as-image'
        self.bt_save_as_image.connect('clicked', self.write_image)
        self.bt_save_as_image.set_tooltip(_('Save as Image'))
        activity_toolbar.insert(self.bt_save_as_image, -1)
        self.bt_save_as_image.show()

        save_as_pdf = ToolButton()
        save_as_pdf.props.icon_name = 'save-as-pdf'
        save_as_pdf.connect('clicked', self._save_as_pdf)
        save_as_pdf.set_tooltip(_('Save as a Book (PDF)'))
        activity_toolbar.insert(save_as_pdf, -1)
        save_as_pdf.show()

        save_as_ogg = ToolButton()
        save_as_ogg.props.icon_name = 'save-as-ogg'
        save_as_ogg.connect('clicked', self.__save_as_ogg_cb)
        save_as_ogg.set_tooltip(_('Save as a Movie (OGG)'))
        activity_toolbar.insert(save_as_ogg, -1)
        save_as_ogg.show()

        activity_button.page.title.connect("focus-in-event", self.on_title)

        scrolled = Gtk.ScrolledWindow()
        # scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.ALWAYS)
        scrolled.add_with_viewport(self.page)
        scrolled.set_kinetic_scrolling(False)
        scrolled.show_all()

        self._slideview = SlideView(self)
        self._slideview.show_all()

        self._notebook = Gtk.Notebook()
        self._notebook.set_show_tabs(False)
        self._notebook.append_page(scrolled, None)
        self._notebook.append_page(self._slideview, None)
        self._notebook.show_all()

        if self._jobject.file_path is None or self._jobject.file_path == '':
            empty_widget = EmptyWidget()
            empty_widget.connect('choose-image', self.__add_image)
            self.set_canvas(empty_widget)
        else:
            self.set_canvas(self._notebook)

        self.show()
        self.metadata['mime_type'] = 'application/x-fototoon-activity'

        self.page.empty_page = handle.object_id is None
        self._key_press_signal_id = None
class Navidad(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle, True)
        cs = ActivityToolbarButton(self)
        c = Gtk.SeparatorToolItem()
        c.set_expand(True)
        lugar = activity.get_bundle_path()
        Gst.init(None)
        self.Musica = Gst.ElementFactory.make('playbin')
        self.Musica.set_property('uri', 'file://' + lugar + '/Musica.ogg')
        self.Musica.set_state(Gst.State.PLAYING)

        self.Toolbar = ToolbarBox()
        self.Toolbar.toolbar.insert(cs, -1)
        self.F = ToolButton('F')
        self.Toolbar.toolbar.insert(self.F, -1)
        self.cambiado = False
        self.cambiadoc = False

        self.E = ToolButton('E')
        self.Toolbar.toolbar.insert(self.E, -1)

        self.L = ToolButton('L')
        self.Toolbar.toolbar.insert(self.L, -1)

        self.I = ToolButton('I')
        self.Toolbar.toolbar.insert(self.I, -1)
        self.Z = ToolButton('Z')
        self.Toolbar.toolbar.insert(self.Z, -1)

        self.N = ToolButton('N')
        self.Toolbar.toolbar.insert(self.N, -1)

        self.AA = ToolButton('A')
        self.Toolbar.toolbar.insert(self.AA, -1)

        self.V = ToolButton('V')
        self.Toolbar.toolbar.insert(self.V, -1)

        self.II = ToolButton('I')
        self.Toolbar.toolbar.insert(self.II, -1)

        self.D = ToolButton('D')
        self.Toolbar.toolbar.insert(self.D, -1)

        self.AAA = ToolButton('A')
        self.Toolbar.toolbar.insert(self.AAA, -1)

        self.DD = ToolButton('D')
        self.Toolbar.toolbar.insert(self.DD, -1)
        self.cambiar()
        Stop = StopButton(self)
        b = Gtk.SeparatorToolItem()
        b.set_expand(True)

        self.Toolbar.toolbar.insert(Stop, -1)
        self.vox = Gtk.EventBox()
        vs = Gtk.VBox()
        self.vox.add(vs)
        Imagen = Gtk.Image()
        Imagen.set_from_file('Imagen.svg')
        bs = Gtk.TextBuffer()
        bs.set_text(
            'El equipo de CeibalJAM! te desea feliz navidad y prospero año nuevo'
        )
        entry = Gtk.TextView.new_with_buffer(bs)
        fuente = Pango.FontDescription('11')
        entry.set_editable(False)
        entry.modify_font(fuente)
        self.vox.connect('button-release-event', self.color, entry)
        self.color('a', 'b', entry)
        self.label = Gtk.Label(label=saber())
        self.label.modify_font(fuente)
        vs.pack_start(self.label, False, False, 0)
        color(self.label)
        vs.pack_end(entry, False, False, 0)
        self.set_canvas(self.vox)
        vs.pack_start(Imagen, True, True, 0)
        self.set_toolbar_box(self.Toolbar)
        self.show_all()
        GObject.timeout_add(37500, self.sonido)

    def sonido(self):
        self.Musica.set_state(Gst.State.NULL)
        self.Musica.set_state(Gst.State.PLAYING)
        GObject.timeout_add(37500, self.sonido)

    def color(self, widget=None, event=None, entrada=None):
        colores = ["#FA2635", "#33DA40"]
        cc = random.choice(colores)
        entrada.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(cc))
        self.vox.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(cc))
        self.vox.show_all()
        GObject.timeout_add(500, self.color, 'a', 'b', entrada)

    def cc(self, a=None, b=None, c=None, d=None):
        if self.cambiadoc:
            self.cambiado = True
        if not self.cambiadoc:
            self.cambiado = False

    def cambiar(self, a=None, b=None):
        if not self.cambiado:
            """ a cambiar """
            self.F.set_icon_name('Fr')  # 1
            self.L.set_icon_name('Lr')  # 3
            self.Z.set_icon_name('Zr')  #
            self.AA.set_icon_name('Ar')  #
            self.II.set_icon_name('Ir')  #
            self.AAA.set_icon_name('Ar')  #
            """ VUELVEN """
            self.E.set_icon_name('E')  # Feliz Navidad #
            self.I.set_icon_name('I')
            self.N.set_icon_name('N')
            self.V.set_icon_name('V')
            self.D.set_icon_name('D')
            self.DD.set_icon_name('D')
            self.cambiadoc = True
            self.Toolbar.show_all()
        if self.cambiado:
            """ a cambiar """
            self.F.set_icon_name('F')  # 1
            self.L.set_icon_name('L')  # 3
            self.Z.set_icon_name('Z')  #
            self.AA.set_icon_name('A')  #
            self.II.set_icon_name('I')  #
            self.AAA.set_icon_name('A')  #
            """ VUELVEN """
            self.E.set_icon_name('Er')  # Feliz Navidad #
            self.I.set_icon_name('Ir')
            self.N.set_icon_name('Nr')
            self.V.set_icon_name('Vr')
            self.D.set_icon_name('Dr')
            self.DD.set_icon_name('Dr')
            self.cambiadoc = False
            self.Toolbar.show_all()
        GObject.timeout_add(500, self.cc)
        GObject.timeout_add(500, self.cambiar)
Ejemplo n.º 19
0
class PhysicsActivity(activity.Activity):
    def __init__(self, handle):
        super(PhysicsActivity, self).__init__(handle)
        self._collab = CollabWrapper(self)
        self._collab.message.connect(self.__message_cb)
        self.metadata['mime_type'] = 'application/x-physics-activity'
        self.add_events(Gdk.EventMask.ALL_EVENTS_MASK
                        | Gdk.EventMask.VISIBILITY_NOTIFY_MASK)

        self.connect('visibility-notify-event', self._focus_event)
        self.connect('window-state-event', self._window_event)

        self.game = PhysicsGame(self)
        self.game.canvas = sugargame.canvas.PygameCanvas(
            self, main=self.game.run, modules=[pygame.display, pygame.font])

        self.preview = None
        self._sample_window = None

        self._notebook = Gtk.Notebook(show_tabs=False)
        self._notebook.add(self.game.canvas)

        w = Gdk.Screen.width()
        h = Gdk.Screen.height() - 2 * GRID_CELL_SIZE

        self.game.canvas.set_size_request(w, h)

        self._constructors = {}
        self.build_toolbar()

        self.set_canvas(self._notebook)
        Gdk.Screen.get_default().connect('size-changed', self.__configure_cb)

        self.show_all()
        self._collab.setup()

    def get_data(self):
        """ FIXME: not implemented, thus objects created before
        sharing starts are not shared """
        return dict()

    def set_data(self, data):
        pass

    def __configure_cb(self, event):
        ''' Screen size has changed '''
        self.write_file(
            os.path.join(activity.get_activity_root(), 'data', 'data'))
        w = Gdk.Screen.width()
        h = Gdk.Screen.height() - 2 * GRID_CELL_SIZE
        pygame.display.set_mode((w, h), pygame.RESIZABLE)
        self.read_file(
            os.path.join(activity.get_activity_root(), 'data', 'data'))

    def read_file(self, file_path):
        self.game.read_file(file_path)

    def write_file(self, file_path):
        self.game.write_file(file_path)

    def get_preview(self):
        ''' Custom preview code to get image from pygame. '''
        return self.game.canvas.get_preview()

    def build_toolbar(self):
        self.max_participants = 4

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        create_toolbar = ToolbarButton()
        create_toolbar.props.page = Gtk.Toolbar()
        create_toolbar.props.icon_name = 'magicpen'
        create_toolbar.props.label = _('Create')
        toolbar_box.toolbar.insert(create_toolbar, -1)
        self._insert_create_tools(create_toolbar)

        color = ColorToolButton('color')
        color.connect('notify::color', self.__color_notify_cb)
        toolbar_box.toolbar.insert(color, -1)
        color.show()

        random = ToggleToolButton('colorRandom')
        random.set_tooltip(_('Toggle random color'))
        toolbar_box.toolbar.insert(random, -1)
        random.set_active(True)
        random.connect('toggled', self.__random_toggled_cb)
        random.show()

        color.random = random
        random.color = color

        random.timeout_id = GLib.timeout_add(100, self.__timeout_cb, random)

        self._insert_stop_play_button(toolbar_box.toolbar)

        clear_trace = ToolButton('clear-trace')
        clear_trace.set_tooltip(_('Clear Trace Marks'))
        clear_trace.set_accelerator(_('<ctrl>x'))
        clear_trace.connect('clicked', self.clear_trace_cb)
        clear_trace.set_sensitive(False)
        toolbar_box.toolbar.insert(clear_trace, -1)
        clear_trace.show()
        self.clear_trace = clear_trace

        self._insert_clear_all_button(toolbar_box.toolbar)

        load_example = ToolButton('load-sample')
        load_example.set_tooltip(_('Show sample projects'))
        load_example.connect('clicked', self._create_store)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        toolbar_box.toolbar.insert(load_example, -1)
        load_example.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_size_request(0, -1)
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop = StopButton(self)
        toolbar_box.toolbar.insert(stop, -1)
        stop.show()

        separator = Gtk.SeparatorToolItem()
        activity_button.props.page.insert(separator, -1)
        separator.show()

        export_json = ToolButton('save-as-json')
        export_json.set_tooltip(_('Export tracked objects to journal'))
        export_json.connect('clicked', self._export_json_cb)
        activity_button.props.page.insert(export_json, -1)
        export_json.show()

        load_project = ToolButton('load-project')
        load_project.set_tooltip(_('Load project from journal'))
        load_project.connect('clicked', self._load_project)
        activity_button.props.page.insert(load_project, -1)
        load_project.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()
        create_toolbar.set_expanded(True)
        return toolbar_box

    def __color_notify_cb(self, button, pdesc):
        """ when a color is chosen;
        change world object add color,
        and change color of buttons. """

        color = button.get_color()
        self._set_color(color)
        button.random.set_active(False)
        button.random.get_icon_widget().set_stroke_color(self._rgb8x(color))

    def __random_toggled_cb(self, random):
        if random.props.active:
            self._random_on(random)
        else:
            self._random_off(random)

    def _random_on(self, random):
        """ when random is turned on;
        reset world object add color,
        and begin watching for changed world object add color. """

        self.game.world.add.reset_color()

        if random.timeout_id is None:
            random.timeout_id = GLib.timeout_add(100, self.__timeout_cb,
                                                 random)
            self.__timeout_cb(random)

    def _random_off(self, random):
        """ when random is turned off;
        change world object add color back to chosen color,
        change color of buttons,
        and stop watching for changed world object add color. """

        color = random.color.get_color()
        self._set_color(color)
        random.get_icon_widget().set_stroke_color(self._rgb8x(color))

        if random.timeout_id is not None:
            GLib.source_remove(random.timeout_id)
            random.timeout_id = None

    def __timeout_cb(self, random):
        """ copy the next color to the random button stroke color. """
        if hasattr(self.game, "world"):
            color = self.game.world.add.next_color()
            random.get_icon_widget().set_stroke_color('#%.2X%.2X%.2X' % color)
        return True

    def _set_color(self, color):
        """ set world object add color. """
        self.game.world.add.set_color(self._rgb8(color))

    def _rgb8x(self, color):
        """ convert a Gdk.Color into hex triplet. """
        return '#%.2X%.2X%.2X' % self._rgb8(color)

    def _rgb8(self, color):
        """ convert a Gdk.Color into an 8-bit RGB tuple. """
        return (color.red / 256, color.green / 256, color.blue / 256)

    def can_close(self):
        self.preview = self.get_preview()
        self.game.running = False
        return True

    def _insert_stop_play_button(self, toolbar):

        self.stop_play_toolbar = ToolbarButton()
        st_toolbar = self.stop_play_toolbar
        st_toolbar.props.page = Gtk.Toolbar()
        st_toolbar.props.icon_name = 'media-playback-stop'

        self.stop_play_state = True
        self.stop_play = ToolButton('media-playback-stop')
        self.stop_play.set_tooltip(_('Stop'))
        self.stop_play.set_accelerator(_('<ctrl>space'))
        self.stop_play.connect('clicked', self.stop_play_cb)
        self._insert_item(st_toolbar, self.stop_play)
        self.stop_play.show()

        slowest_button = RadioToolButton(group=None)
        slowest_button.set_icon_name('slow-walk-milton-raposo')
        slowest_button.set_tooltip(_('Run slower'))
        slowest_button.connect('clicked', self._set_fps_cb, SLOWEST_FPS)
        self._insert_item(st_toolbar, slowest_button)
        slowest_button.show()

        slow_button = RadioToolButton(group=slowest_button)
        slow_button.set_icon_name('walking')
        slow_button.set_tooltip(_('Run slow'))
        slow_button.connect('clicked', self._set_fps_cb, SLOW_FPS)
        self._insert_item(st_toolbar, slow_button)
        slow_button.show()

        fast_button = RadioToolButton(group=slowest_button)
        fast_button.set_icon_name('running')
        fast_button.set_tooltip('Run fast')
        fast_button.connect('clicked', self._set_fps_cb, FAST_FPS)
        self._insert_item(st_toolbar, fast_button)
        fast_button.show()
        fast_button.set_active(True)

        toolbar.insert(self.stop_play_toolbar, -1)
        self.stop_play_toolbar.show_all()

    def _set_fps_cb(self, button, value):
        self.game.set_game_fps(value)

    def _insert_clear_all_button(self, toolbar):
        self.clear_all = ToolButton('clear_all')
        self.clear_all.set_tooltip(_('Erase All'))
        self.clear_all.set_accelerator(_('<ctrl>a'))
        self.clear_all.connect('clicked', self.clear_all_cb)
        toolbar.insert(self.clear_all, -1)
        self.clear_all.set_sensitive(False)
        self.clear_all.show()

    def _insert_item(self, toolbar, item, pos=-1):
        if hasattr(toolbar, 'insert'):
            toolbar.insert(item, pos)
        else:
            toolbar.props.page.insert(item, pos)

    def _insert_create_tools(self, create_toolbar):
        # Make + add the component buttons
        self.radioList = {}
        for i, c in enumerate(tools.allTools):
            if i == 0:
                button = RadioToolButton(group=None)
                firstbutton = button
            else:
                button = RadioToolButton(group=firstbutton)
            button.set_icon_name(c.icon)
            button.set_tooltip(c.toolTip)
            button.set_accelerator(c.toolAccelerator)
            button.connect('clicked', self.radioClicked)
            palette = self._build_palette(c)
            if palette is not None:
                palette.show()
                button.get_palette().set_content(palette)
            self._insert_item(create_toolbar, button, -1)
            button.show()
            self.radioList[button] = c.name
            if hasattr(c, 'constructor'):
                self._constructors[c.name] = \
                    self.game.toolList[c.name].constructor

    def __icon_path(self, name):
        activity_path = activity.get_bundle_path()
        icon_path = os.path.join(activity_path, 'icons', name + '.svg')
        return icon_path

    def _build_palette(self, tool):
        if tool.palette_enabled:
            if tool.palette_mode == tools.PALETTE_MODE_ICONS:
                grid = Gtk.Grid()
                for s, settings in enumerate(tool.palette_settings):
                    self.game.toolList[tool.name].buttons.append([])
                    for i, icon_value in enumerate(settings['icon_values']):
                        if i == 0:
                            button = RadioToolButton(group=None)
                            firstbutton = button
                        else:
                            button = RadioToolButton(group=firstbutton)
                        button.set_icon_name(settings['icons'][i])
                        button.connect('clicked', self._palette_icon_clicked,
                                       tool.name, s, settings['name'],
                                       icon_value)
                        grid.attach(button, i, s, 1, 1)
                        self.game.toolList[tool.name].buttons[s].append(button)
                        button.show()
                        if settings['active'] == settings['icons'][i]:
                            button.set_icon_name(settings['icons'][i] +
                                                 '-selected')
                            button.set_active(True)
                return grid
        else:
            return None

    def _palette_icon_clicked(self, widget, toolname, s, value_name, value):
        for tool in tools.allTools:
            if tool.name == toolname:
                # Radio buttons are not highlighting in the palette
                # so adding highlight by hand
                # See http://bugs.sugarlabs.org/ticket/305
                setting = self.game.toolList[tool.name].palette_settings[s]
                for i, button in enumerate(
                        self.game.toolList[tool.name].buttons[s]):
                    icon_name = setting['icons'][i]
                    if button == widget:
                        button.set_icon_name(icon_name + '-selected')
                    else:
                        button.set_icon_name(icon_name)
                if hasattr(tool, 'palette_data_type'):
                    tool.palette_data_type = value
                else:
                    tool.palette_data[value_name] = value

    def clear_trace_alert_cb(self, alert, response):
        self.remove_alert(alert)
        if response is Gtk.ResponseType.OK:
            self.game.full_pos_list = [[] for _ in self.game.full_pos_list]
            self.game.tracked_bodies = 0

    def clear_trace_cb(self, button):
        clear_trace_alert = ConfirmationAlert()
        clear_trace_alert.props.title = _('Are You Sure?')
        clear_trace_alert.props.msg = \
            _('All trace points will be erased. This cannot be undone!')
        clear_trace_alert.connect('response', self.clear_trace_alert_cb)
        self.add_alert(clear_trace_alert)

    def stop_play_cb(self, button):
        pygame.event.post(
            pygame.event.Event(pygame.USEREVENT, action='stop_start_toggle'))
        self.stop_play_state = not self.stop_play_state

        if self.stop_play_state:
            self.stop_play.set_icon_name('media-playback-stop')
            self.stop_play.set_tooltip(_('Stop'))

            self.stop_play_toolbar.set_icon_name('media-playback-stop')
        else:
            self.stop_play.set_icon_name('media-playback-start')
            self.stop_play.set_tooltip(_('Start'))

            self.stop_play_toolbar.set_icon_name('media-playback-start')

    def clear_all_cb(self, button):
        def clear_all_alert_cb(alert, response_id):
            self.remove_alert(alert)
            if response_id is Gtk.ResponseType.OK:
                pygame.event.post(
                    pygame.event.Event(pygame.USEREVENT, action='clear_all'))

        if len(self.game.world.world.bodies) > 2:
            clear_all_alert = ConfirmationAlert()
            clear_all_alert.props.title = _('Are You Sure?')
            clear_all_alert.props.msg = \
                _('All your work will be discarded. This cannot be undone!')
            clear_all_alert.connect('response', clear_all_alert_cb)
            self.add_alert(clear_all_alert)

    def radioClicked(self, button):
        pygame.event.post(
            pygame.event.Event(pygame.USEREVENT,
                               action=self.radioList[button]))

    def _focus_event(self, event, data=None):
        ''' Send focus events to pygame to allow it to idle when in
        background. '''
        if data.state == Gdk.VisibilityState.FULLY_OBSCURED:
            pygame.event.post(
                pygame.event.Event(pygame.USEREVENT, action='focus_out'))
        else:
            self.game.show_fake_cursor = True
            pygame.event.post(
                pygame.event.Event(pygame.USEREVENT, action='focus_in'))

    def _export_json_cb(self, button):
        jobject = datastore.create()
        jobject.metadata['title'] = _('Physics export')
        jobject.metadata['mime_type'] = 'text/plain'

        tmp_dir = os.path.join(self.get_activity_root(), 'instance')
        fd, file_path = tempfile.mkstemp(dir=tmp_dir)
        os.close(fd)

        self.game.world.json_save(file_path)

        jobject.set_file_path(file_path)
        datastore.write(jobject)

    def _window_event(self, window, event):
        ''' Send focus out event to pygame when switching to a desktop
        view. '''
        if event.changed_mask & Gdk.WindowState.ICONIFIED:
            pygame.event.post(
                pygame.event.Event(pygame.USEREVENT, action='focus_out'))

    def _restore_cursor(self):
        ''' No longer waiting, so restore standard cursor. '''
        if not hasattr(self, 'get_window'):
            return
        self.get_window().set_cursor(self.old_cursor)

    def _waiting_cursor(self):
        ''' Waiting, so set watch cursor. '''
        if not hasattr(self, 'get_window'):
            return
        self.old_cursor = self.get_window().get_cursor()
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))

    def __message_cb(self, collab, buddy, msg):
        ''' Data is passed as tuples: cmd:text '''
        action = msg.get('action')
        if action != 'text':
            return

        text = msg['text']
        dispatch_table = {
            'C': self._construct_shared_circle,
            'B': self._construct_shared_box,
            'T': self._construct_shared_triangle,
            'P': self._construct_shared_polygon,
            'M': self._construct_shared_magicpen,
            'j': self._add_shared_joint,
            'p': self._add_shared_pin,
            'm': self._add_shared_motor,
            't': self._add_shared_track,
            'c': self._add_shared_chain,
        }
        logging.debug('<<< %s' % (text[0]))
        dispatch_table[text[0]](text[2:])

    def _construct_shared_circle(self, data):
        circle_data = json.loads(data)
        pos = circle_data[0]
        radius = circle_data[1]
        density = circle_data[2]
        restitution = circle_data[3]
        friction = circle_data[4]
        self._constructors['Circle'](pos,
                                     radius,
                                     density,
                                     restitution,
                                     friction,
                                     share=False)

    def _construct_shared_box(self, data):
        box_data = json.loads(data)
        pos1 = box_data[0]
        pos2 = box_data[1]
        density = box_data[2]
        restitution = box_data[3]
        friction = box_data[4]
        self._constructors['Box'](pos1,
                                  pos2,
                                  density,
                                  restitution,
                                  friction,
                                  share=False)

    def _construct_shared_triangle(self, data):
        triangle_data = json.loads(data)
        pos1 = triangle_data[0]
        pos2 = triangle_data[1]
        density = triangle_data[2]
        restitution = triangle_data[3]
        friction = triangle_data[4]
        self._constructors['Triangle'](pos1,
                                       pos2,
                                       density,
                                       restitution,
                                       friction,
                                       share=False)

    def _construct_shared_polygon(self, data):
        polygon_data = json.loads(data)
        vertices = polygon_data[0]
        density = polygon_data[1]
        restitution = polygon_data[2]
        friction = polygon_data[3]
        self._constructors['Polygon'](vertices,
                                      density,
                                      restitution,
                                      friction,
                                      share=False)

    def _construct_shared_magicpen(self, data):
        magicpen_data = json.loads(data)
        vertices = magicpen_data[0]
        density = magicpen_data[1]
        restitution = magicpen_data[2]
        friction = magicpen_data[3]
        self._constructors['Magicpen'](vertices,
                                       density,
                                       restitution,
                                       friction,
                                       share=False)

    def _add_shared_joint(self, data):
        joint_data = json.loads(data)
        pos1 = joint_data[0]
        pos2 = joint_data[1]
        self._constructors['Joint'](pos1, pos2, share=False)

    def _add_shared_pin(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        self._constructors['Pin'](pos, share=False)

    def _add_shared_motor(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        speed = joint_data[1]
        self._constructors['Motor'](pos, speed, share=False)

    def _add_shared_track(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        color = joint_data[1]
        self._constructors['Track'](pos, color, share=False)

    def _add_shared_chain(self, data):
        joint_data = json.loads(data)
        vertices = joint_data[0]
        link_length = joint_data[1]
        radius = joint_data[2]
        self._constructors['Chain'](vertices, link_length, radius, share=False)

    def send_event(self, text):
        self._collab.post(dict(action='text', text=text))

    def _load_project(self, button):
        chooser = ObjectChooser(parent=self)
        result = chooser.run()
        if result == Gtk.ResponseType.ACCEPT:
            dsobject = chooser.get_selected_object()
            file_path = dsobject.get_file_path()
            self.__load_game(file_path, True)
            chooser.destroy()

    def __load_game(self, file_path, journal=False):
        confirmation_alert = ConfirmationAlert()
        confirmation_alert.props.title = _('Are You Sure?')
        confirmation_alert.props.msg = \
            _('All your work will be discarded. This cannot be undone!')

        def action(alert, response):
            self.remove_alert(alert)
            if response is not Gtk.ResponseType.OK:
                return

            try:
                f = open(file_path, 'r')
                # Test if the file is valid project.
                json.loads(f.read())
                f.close()

                self.read_file(file_path)
            except:
                title = _('Load project from journal')
                if not journal:
                    title = _('Load example')
                msg = _('Error: Cannot open Physics project from this file.')
                alert = NotifyAlert(5)
                alert.props.title = title
                alert.props.msg = msg
                alert.connect('response',
                              lambda alert, response: self.remove_alert(alert))
                self.add_alert(alert)

        confirmation_alert.connect('response', action)
        self.add_alert(confirmation_alert)

    def _create_store(self, widget=None):
        if self._sample_window is None:
            self._sample_box = Gtk.EventBox()

            vbox = Gtk.VBox()
            self._sample_window = Gtk.ScrolledWindow()
            self._sample_window.set_policy(Gtk.PolicyType.NEVER,
                                           Gtk.PolicyType.AUTOMATIC)
            self._sample_window.set_border_width(4)

            self._sample_window.set_size_request(Gdk.Screen.width() / 2,
                                                 Gdk.Screen.height() / 2)
            self._sample_window.show()

            store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)

            icon_view = Gtk.IconView()
            icon_view.set_model(store)
            icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE)
            icon_view.connect('selection-changed', self._sample_selected,
                              store)
            icon_view.set_pixbuf_column(0)
            icon_view.grab_focus()
            self._sample_window.add(icon_view)
            icon_view.show()
            self._fill_samples_list(store)

            title = Gtk.HBox()
            title_label = Gtk.Label(_("Select a sample..."))
            separator = Gtk.HSeparator()
            separator.props.expand = True
            separator.props.visible = False

            btn = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL)
            btn.connect('clicked', self._cancel_clicked_cb)

            title.pack_start(title_label, False, False, 5)
            title.pack_start(separator, False, False, 0)
            title.pack_end(btn, False, False, 0)

            vbox.pack_start(title, False, False, 5)
            vbox.pack_end(self._sample_window, True, True, 0)

            self._sample_box.add(vbox)
            self._sample_box.show_all()
            self._notebook.add(self._sample_box)

        self._notebook.set_current_page(1)

    def _cancel_clicked_cb(self, button=None):
        self._notebook.set_current_page(0)

    def _get_selected_path(self, widget, store):
        try:
            iter_ = store.get_iter(widget.get_selected_items()[0])
            image_path = store.get(iter_, 1)[0]

            return image_path, iter_
        except:
            return None

    def _sample_selected(self, widget, store):
        selected = self._get_selected_path(widget, store)

        if selected is None:
            self._selected_sample = None
            self._cancel_clicked_cb()
            return

        image_path, _iter = selected
        iter_ = store.get_iter(widget.get_selected_items()[0])
        image_path = store.get(iter_, 1)[0]

        self._selected_sample = image_path
        self._cancel_clicked_cb()

        self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH))
        GLib.idle_add(self._sample_loader)

    def _sample_loader(self):
        # Convert from thumbnail path to sample path
        basename = os.path.basename(self._selected_sample)[:-4]
        file_path = os.path.join(activity.get_bundle_path(), 'samples',
                                 basename + '.json')
        self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR))
        self.__load_game(file_path)

    def _fill_samples_list(self, store):
        '''
        Append images from the artwork_paths to the store.
        '''
        for filepath in self._scan_for_samples():
            pixbuf = None
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filepath, 100, 100)
            store.append([pixbuf, filepath])

    def _scan_for_samples(self):
        samples = sorted(
            glob.glob(
                os.path.join(activity.get_bundle_path(), 'samples',
                             'thumbnails', '*.png')))
        return samples
Ejemplo n.º 20
0
class FavoritePage(Gtk.Toolbar):

    def __init__(self, button, toolbar, view_icon, favorite_icon, label):
        Gtk.Toolbar.__init__(self)

        self.toolbar = toolbar
        self.button = button
        self.view_icon = view_icon
        self.favorite_icon = favorite_icon

        self.set_view_icon = ToolButton(view_icon)
        self.set_view_icon.set_tooltip(_('Set toolbar icon'))
        self.set_view_icon.connect('clicked', self.set_icon, False)

        self.set_favorite_icon = ToolButton(favorite_icon)
        self.set_favorite_icon.set_tooltip(_('Set the icon of favorites list'))
        self.set_favorite_icon.connect('clicked', self.set_icon, True)

        entry_toolitem = Gtk.ToolItem()

        self.favorite_name_entry = Gtk.Entry()
        self.favorite_name_entry.set_placeholder_text(_('Favorite view name'))
        width = Gdk.Screen.width() - (style.STANDARD_ICON_SIZE * 12)
        entry_toolitem.set_size_request(width, 55)
        self.favorite_name_entry.set_text(label)
        self.favorite_name_entry.connect("activate", self.edited_view_name)
        entry_toolitem.add(self.favorite_name_entry)

        label = Gtk.Label(_('Name of favorite view') + '\t')
        label.modify_fg(Gtk.StateType.NORMAL, Gdk.color_parse('white'))
        tool_label = Gtk.ToolItem()
        tool_label.add(label)

        self.remove_btn = ToolButton('list-remove')
        self.remove_btn.set_tooltip(_('Remove favorite view'))
        self.remove_btn.connect('clicked', self.remove_view)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)

        self.insert(self.set_view_icon, -1)
        self.insert(self.set_favorite_icon, -1)
        self.insert(tool_label, -1)
        self.insert(entry_toolitem, -1)
        self.insert(separator, -1)
        self.insert(self.remove_btn, -1)
        self.show_all()

    def set_icon(self, widget, favorite=False, window=None):
        if not window:
            dialog = IconDialog()
            dialog.show_all()
            dialog.connect('destroy', self.set_icon, favorite, dialog)

        if window:
            response = window.get_icon()
            if not response:
                return
            if favorite:
                self.set_favorite_icon.set_icon_name(response)
                self.toolbar._favorite_icons[self.button] = response
            else:
                self.set_view_icon.set_icon_name(response)
                self.toolbar._view_icons[self.button] = response
                self.button.set_icon_name(response)

            self.toolbar.save_to_gsettings(True)

    def remove_view(self, widget):
        self.button.set_expanded(False)
        self.toolbar.remove_item(self.button)

    def edited_view_name(self, entry):
        label = entry.get_text()
        self.toolbar._favorite_names[self.button] = label

        self.toolbar.save_to_gsettings(False, True)
Ejemplo n.º 21
0
class FreeFromMalariaActivity(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self.activity_state = {}
        self._name = handle
        self.set_title(_("Free From Malaria"))

        restartTitle = _("End User Licence")
        restartText = _(
            "The materials in this game are in no way intended to replace or supersede professional medical care, advice, diagnosis or treatment of a doctor. The game only provides general advice on Malaria. This advice may not apply to everyone in every locality. If you notice medical symptoms or feel ill, you should consult your doctor. Please seek further advice from your local health authority for further information about Malaria.All information provided in this activity has been produced using peer reviewed scientific and health documentation. By clicking OK you agree that this game should be used for general educational and information purposes only and is not intended to replace medical advice or act as a diagnosis tool. For more details please visit http://worldclassproject.org.uk/."
        )
        dialog = self.create_dialog_ok(restartTitle, restartText)
        result = dialog.run()
        dialog.destroy()

        #init the controller
        #game controller

        if handle.object_id == None:
            print "Launched from home."
        else:
            print "Journal resume."

        # Set title for our Activity
        self.set_title('Free From Malaria')

        # Attach sugar toolbox (Share, ...)
        # Use old <= 0.84 toolbar design
        toolbox = ToolbarBox()
        view_tool_bar = toolbox.get_toolbar()

        #for debug only
        #		self.previous_chapter = ToolButton('gtk-media-forward-rtl')
        #		self.previous_chapter.set_tooltip("Previous Chapter")
        #		self.previous_chapter_id = self.previous_chapter.connect('clicked', self.previous_chapter_clicked)
        #		view_tool_bar.insert(self.previous_chapter, 0)
        #		self.previous_chapter.show()
        ########################################
        self.previous_scene_button = ToolButton('previous')
        self.previous_scene_button.set_tooltip("Previous Scene")
        self.previous_scene_id = self.previous_scene_button.connect(
            'clicked', self.previous_scene_clicked)
        view_tool_bar.insert(self.previous_scene_button, 1)
        self.previous_scene_button.show()
        ###########print "CHANGING"
        self.next_scene = ToolButton('next')
        self.next_scene.set_tooltip("Next Scene")
        self.next_scene_id = self.next_scene.connect('clicked',
                                                     self.next_scene_clicked)
        view_tool_bar.insert(self.next_scene, 2)
        self.next_scene.show()
        ###########
        #disable comic buttons in the main menu
        self.set_navigation(False)
        ######################
        self.sound = ToolButton('audio-volume-high')
        homeString = _("Enable/Disable Sound")
        self.sound.set_tooltip(homeString)
        self.sound_id = self.sound.connect('clicked', self.sound_clicked)
        view_tool_bar.insert(self.sound, 3)
        self.sound.show()
        #############################
        ###########
        self.reload = ToolButton('reload')
        restart = _("Restart")
        self.reload.set_tooltip(restart)
        self.reload_id = self.reload.connect('clicked', self.reload_clicked)
        view_tool_bar.insert(self.reload, 4)
        self.reload.show()
        ######################

        ######################
        self.home = ToolButton('go-home')
        homeString = _("Go to main menu")
        self.home.set_tooltip(homeString)
        self.home_id = self.home.connect('clicked', self.home_clicked)
        view_tool_bar.insert(self.home, 5)
        self.home.show()
        #############################

        view_tool_bar.show()
        # toolbox.add_toolbar('Game Control', view_tool_bar)
        self.set_toolbar_box(toolbox)
        toolbox.show()
        activity_toolbar = toolbox.get_toolbar()
        self.max_participants = 1

        #global game state

        ##restore previous session
        self.controller = controller.GameController.GlobalGameController(self)

        self.read_and_parse_prefs(os.environ['SUGAR_ACTIVITY_ROOT'] +
                                  '/data/defaults')

        #welcomeview = WelcomeScreen.GameWelcomeScreen(self.controller);

        #self.create_new_window(welcomeview)
#		self.mywindow = welcomeview.get_window()
#		self.w_child = self.mywindow.get_child()
#		self.widget = self.w_child
#		self.pack_and_show()

    def next_scene_clicked(self, event):

        self.controller.next_scene()

    def previous_scene_clicked(self, event):
        self.controller.previous_scene()

    def reload_clicked(self, event):
        restartTitle = _("Restart The Story?")
        restartText = _("Are you sure you want to restart the story?")
        dialog = self.create_dialog(restartTitle, restartText)
        result = dialog.run()
        dialog.destroy()

        if result == Gtk.ResponseType.YES:
            self.controller.reload()

    def home_clicked(self, event):
        restartTitle = _("Restart The Main Menu?")
        restartText = _("Are you sure you want to return to the main menu?")
        dialog = self.create_dialog(restartTitle, restartText)

        result = dialog.run()
        dialog.destroy()

        if result == Gtk.ResponseType.YES:
            self.controller.play_game("Welcome", None)

    def sound_clicked(self, event):
        if (self.controller.get_sound() == True):
            self.sound.set_icon_name("audio-volume-muted")
            self.controller.set_sound(False)
        else:
            self.controller.set_sound(True)
            self.sound.set_icon_name("audio-volume-high")

    def create_dialog(self, title, message):
        dialog = Gtk.MessageDialog(parent=None,
                                   buttons=Gtk.ButtonsType.YES_NO,
                                   flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
                                   type=Gtk.MessageType.QUESTION,
                                   message_format=message)
        dialog.set_title(title)
        return dialog

    def create_dialog_ok(self, title, message):
        dialog = Gtk.MessageDialog(parent=None,
                                   buttons=Gtk.ButtonsType.OK,
                                   flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
                                   type=Gtk.MessageType.QUESTION,
                                   message_format=message)
        dialog.set_title(title)
        return dialog

    ##disables the nav buttons when comic is not being shown
    def set_navigation(self, switch):

        self.previous_scene_button.set_sensitive(switch)
        self.next_scene.set_sensitive(switch)

    def create_new_window(self, view):

        #self.view = view;
        self.mywindow = view.get_window()

        self.w_child = self.mywindow.get_child()

        self.activity_widget = self.w_child

        #		if(self.widget==None or self.w_child==None):
        #			raise RuntimeError("No widget available")
        self.pack_and_show()

    def pack_and_show(self):
        # Create the main container
        self._main_view = Gtk.VBox()

        # Import our class gtktest():

        # Step 1: Load class, which creates gtktest.widget
        #self.gtktest = devtest2(self)

        # Step 2: Remove the widget's parent
        if self.activity_widget.get_parent():
            self.activity_widget.get_parent().remove(self.activity_widget)

        # Step 3: We attach that widget to our window
        self._main_view.pack_start(self.activity_widget, True, True, 0)

        # Display everything
        self.activity_widget.show()
        self._main_view.show()
        self.set_canvas(self._main_view)
        self.show_all()

    def read_and_parse_prefs(self, file_path):
        #Parse and set preference data from a given file.
        #file_path=os.environ['SUGAR_ACTIVITY_ROOT'] + '/data/defaults'
        #file_path=os.environ['SUGAR_ACTIVITY_ROOT'] + '/data/defaults';
        print "Restoring ", file_path
        try:
            read_file = open(file_path, 'r')
            self.activity_state = json.loads(read_file.read())

            if self.activity_state.has_key('comic_index'):

                comic_index = self.activity_state['comic_index']
                #					print "Restoring with index ",comic_index
                self.controller.set_comic_index(comic_index)
                print "Restored COMIC SCENE from ", file_path

            if self.activity_state.has_key('current_language_code'):
                current_language = self.activity_state['current_language_code']
                self.controller.load_language_by_code(current_language)
                print "Restored LANGUAGE from ", file_path
            self.controller.update_scene()
            read_file.close()

        except IOError:  # as (errno, strerror):
            print "Error: Preferences error"
        except AttributeError:  # as (errno):
            print "Warning: controller not initialised and prefs called. {0}"
        except:
            raise

    def read_file(self, file_path):
        #Read state from datastore.

        self.read_and_parse_prefs(file_path)

    def write_file(self, file_path):
        #		#"""Write state to journal datastore and to persistent file system.
        #		#"""
        #file_path=os.environ['SUGAR_ACTIVITY_ROOT'] + '/data/defaults';
        #		print "WRITING FILE TO JOURNAL : "+os.environ['SUGAR_ACTIVITY_ROOT'] + '/data/defaults'
        # BUG [ID: 3334629]
        try:
            print "WRITING FILE TO JOURNAL : " + file_path
            self.activity_state[
                'comic_index'] = self.controller.get_comic_index()
            self.activity_state[
                'current_language_code'] = self.controller.get_current_language_code(
                )

            serialised_data = json.dumps(self.activity_state)

            to_journal = file(file_path, 'w')
            try:

                to_journal.write(serialised_data)
            except:
                raise
            finally:
                to_journal.close()

                to_persistent_fs = file(
                    os.environ['SUGAR_ACTIVITY_ROOT'] + '/data/defaults', 'w')

            try:
                to_persistent_fs.write(serialised_data)
            except:
                raise
            finally:
                to_persistent_fs.close()
        except AttributeError:  # BUG [ID: 3334629]
            print "Warning: controller not initialised and trying to write. {0}"
Ejemplo n.º 22
0
class FavoritePage(Gtk.Toolbar):
    def __init__(self, button, toolbar, view_icon, favorite_icon, label):
        Gtk.Toolbar.__init__(self)

        self.toolbar = toolbar
        self.button = button
        self.view_icon = view_icon
        self.favorite_icon = favorite_icon

        self.set_view_icon = ToolButton(view_icon)
        self.set_view_icon.set_tooltip(_('Set toolbar icon'))
        self.set_view_icon.connect('clicked', self.set_icon, False)

        self.set_favorite_icon = ToolButton(favorite_icon)
        self.set_favorite_icon.set_tooltip(_('Set the icon of favorites list'))
        self.set_favorite_icon.connect('clicked', self.set_icon, True)

        entry_toolitem = Gtk.ToolItem()

        self.favorite_name_entry = Gtk.Entry()
        self.favorite_name_entry.set_text(label)
        self.favorite_name_entry.connect("activate", self.edited_view_name)
        entry_toolitem.add(self.favorite_name_entry)

        self.remove_btn = ToolButton('list-remove')
        self.remove_btn.set_tooltip(_('Remove favorite view'))
        self.remove_btn.connect('clicked', self.remove_view)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)

        self.insert(self.set_view_icon, -1)
        self.insert(self.set_favorite_icon, -1)
        if self.toolbar.favorite_names_enabled:
            self.insert(entry_toolitem, -1)
        self.insert(separator, -1)
        self.insert(self.remove_btn, -1)
        self.show_all()

    def set_icon(self, widget, favorite=False, window=None):
        if not window:
            dialog = IconDialog()
            dialog.show_all()
            dialog.connect('destroy', self.set_icon, favorite, dialog)

        if window:
            response = window.get_icon()
            if not response:
                return
            if favorite:
                self.set_favorite_icon.set_icon_name(response)
                self.toolbar._favorite_icons[self.button] = response
            else:
                self.set_view_icon.set_icon_name(response)
                self.toolbar._view_icons[self.button] = response
                self.button.set_icon_name(response)

            self.toolbar.save_to_gconf(True)

    def remove_view(self, widget):
        self.button.set_expanded(False)
        self.toolbar.remove_item(self.button)

    def edited_view_name(self, entry):
        label = entry.get_text()
        self.toolbar._favorite_names[self.button] = label

        self.toolbar.save_to_gconf(False, True)
Ejemplo n.º 23
0
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self.set_title('FotoToon')

        self._max_participants = 1
        self.page = None

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        activity_toolbar = activity_button.page
        toolbar_box.toolbar.insert(activity_button, 0)

        edit_toolbar_btn = ToolbarButton()
        edit_toolbar = Gtk.Toolbar()
        edit_toolbar_btn.props.page = edit_toolbar
        edit_toolbar_btn.props.icon_name = 'toolbar-edit'
        edit_toolbar_btn.label = _('Edit')
        toolbar_box.toolbar.insert(edit_toolbar_btn, -1)

        view_toolbar_btn = ToolbarButton()
        view_toolbar = Gtk.Toolbar()
        view_toolbar_btn.props.page = view_toolbar
        view_toolbar_btn.props.icon_name = 'toolbar-view'
        view_toolbar_btn.label = _('View')
        toolbar_box.toolbar.insert(view_toolbar_btn, -1)

        slideview_btn = ToggleToolButton('slideshow')
        slideview_btn.set_tooltip(_('Slideshow'))
        slideview_btn.set_active(False)
        slideview_btn.connect('clicked', self._switch_view_mode, False)
        view_toolbar.insert(slideview_btn, -1)
        slideview_btn.show()

        slideview_timings_btn = ToggleToolButton('slideshow-stopwatch')
        slideview_timings_btn.set_tooltip(_('Slideshow with Timings'))
        slideview_timings_btn.set_active(False)
        slideview_timings_btn.connect('clicked', self._switch_view_mode, True)
        view_toolbar.insert(slideview_timings_btn, -1)
        slideview_timings_btn.show()

        time_button = ToolButton('stopwatch')
        time_button.set_tooltip(_('Set Image Duration in Slideshow (Seconds)'))
        view_toolbar.insert(time_button, -1)
        time_button.show()

        self._time_spin = Gtk.SpinButton.new_with_range(MIN_TIME, MAX_TIME, 1)
        self._time_spin.connect('value-changed', self.__time_spin_changed_cb)
        self._time_spin.props.value = DEFAULT_TIME
        self._time_spin.props.update_policy = \
            Gtk.SpinButtonUpdatePolicy.IF_VALID

        palette = time_button.get_palette()
        palette.connect('popup', self.__time_button_popup_cb)
        time_button.connect(
            'clicked', lambda *args: palette.popup(immediate=True,
                                                   state=Palette.SECONDARY))

        alignment = Gtk.Alignment()
        alignment.set_padding(style.DEFAULT_PADDING, style.DEFAULT_PADDING,
                              style.DEFAULT_PADDING, style.DEFAULT_PADDING)
        alignment.add(self._time_spin)
        self._time_spin.show()
        palette.set_content(alignment)
        alignment.show()

        fullscreen_btn = ToolButton('view-fullscreen')
        fullscreen_btn.set_tooltip(_('Fullscreen'))
        fullscreen_btn.props.accelerator = '<Alt>Return'
        fullscreen_btn.connect('clicked', lambda w: self.fullscreen())
        view_toolbar.insert(fullscreen_btn, -1)
        fullscreen_btn.show()

        self.set_toolbar_box(toolbar_box)

        toolbar = toolbar_box.toolbar

        self.page = Page()

        self.globes_manager = GlobesManager(toolbar, edit_toolbar, self)

        # fonts
        self._text_button = ToolbarButton()
        self._text_button.props.page = TextToolbar(self.page)
        self._text_button.props.icon_name = 'format-text-size'
        self._text_button.props.label = _('Text')
        self._toolbar_box.toolbar.insert(self._text_button, -1)

        reorder_img_btn = ToolButton('thumbs-view')
        reorder_img_btn.set_icon_name('thumbs-view')
        reorder_img_btn.set_tooltip(_('Change image order'))
        reorder_img_btn.connect('clicked', self.__image_order_cb)
        edit_toolbar.insert(reorder_img_btn, -1)
        reorder_img_btn.show()

        bgchange = ToolButton(icon_name='contract-coordinates')
        bgchange.set_tooltip(_('Edit background image'))
        bgchange.connect('clicked', self.__bgchange_clicked_cb)
        edit_toolbar.insert(bgchange, -1)
        bgchange.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)

        toolbar_box.toolbar.insert(separator, -1)

        stop = StopButton(self)
        toolbar_box.toolbar.insert(stop, -1)

        toolbar_box.show_all()

        # add export button

        separator_2 = Gtk.SeparatorToolItem()
        separator_2.show()
        activity_toolbar.insert(separator_2, -1)

        self.bt_save_as_image = ToolButton()
        self.bt_save_as_image.props.icon_name = 'save-as-image'
        self.bt_save_as_image.connect('clicked', self.write_image)
        self.bt_save_as_image.set_tooltip(_('Save as Image'))
        activity_toolbar.insert(self.bt_save_as_image, -1)
        self.bt_save_as_image.show()

        save_as_pdf = ToolButton()
        save_as_pdf.props.icon_name = 'save-as-pdf'
        save_as_pdf.connect('clicked', self._save_as_pdf)
        save_as_pdf.set_tooltip(_('Save as a Book (PDF)'))
        activity_toolbar.insert(save_as_pdf, -1)
        save_as_pdf.show()

        save_as_ogg = ToolButton()
        save_as_ogg.props.icon_name = 'save-as-ogg'
        save_as_ogg.connect('clicked', self.__save_as_ogg_cb)
        save_as_ogg.set_tooltip(_('Save as a Movie (OGG)'))
        activity_toolbar.insert(save_as_ogg, -1)
        Gst.init(None)
        if Gst.version_string() != 'GStreamer 1.0.10':
            save_as_ogg.show()

        activity_button.page.title.connect("focus-in-event", self.on_title)

        scrolled = Gtk.ScrolledWindow()
        # scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.ALWAYS)
        scrolled.add_with_viewport(self.page)
        scrolled.set_kinetic_scrolling(False)
        scrolled.show_all()

        self._slideview = SlideView(self)
        self._slideview.show_all()

        self._notebook = Gtk.Notebook()
        self._notebook.set_show_tabs(False)
        self._notebook.append_page(scrolled, None)
        self._notebook.append_page(self._slideview, None)
        self._notebook.show_all()

        if self._jobject.file_path is None or self._jobject.file_path == '':
            empty_widget = EmptyWidget()
            empty_widget.connect('choose-image', self.__add_image)
            self.set_canvas(empty_widget)
        else:
            self.set_canvas(self._notebook)

        self.show()
        self.metadata['mime_type'] = 'application/x-fototoon-activity'

        self.page.empty_page = handle.object_id is None
        self._key_press_signal_id = None
Ejemplo n.º 24
0
class DevelopViewToolbar(Gtk.Toolbar):
    __gsignals__ = {
        'theme-changed': (GObject.SIGNAL_RUN_FIRST, None, (str, )),
        'font-size-changed': (GObject.SIGNAL_RUN_FIRST, None, (int, )),
    }

    def __init__(self, _activity):
        GObject.GObject.__init__(self)

        self._activity = _activity
        self.theme_state = "light"
        self.font_size = DEFAULT_FONT_SIZE

        self.theme_toggler = ToolButton('dark-theme')
        self.theme_toggler.connect('clicked', self._toggled_theme)
        self.theme_toggler.set_tooltip('Switch to Dark Theme')
        self.insert(self.theme_toggler, -1)
        self.theme_toggler.show()

        sep = Gtk.SeparatorToolItem()
        self.insert(sep, -1)
        sep.show()

        self.font_plus = ToolButton('zoom-in')
        self.font_plus.connect('clicked', self._font_size_increase)
        self.font_plus.set_tooltip('Zoom in')
        self.insert(self.font_plus, -1)
        self.font_plus.show()

        self.font_minus = ToolButton('zoom-out')
        self.font_minus.connect('clicked', self._font_size_decrease)
        self.font_minus.set_tooltip('Zoom out')
        self.insert(self.font_minus, -1)
        self.font_minus.show()

        self.show()

    def set_theme(self, theme_name):
        self.theme_state = theme_name

        if self.theme_state == "light":
            self.theme_toggler.set_icon_name('dark-theme')
            self.theme_toggler.set_tooltip('Switch to Dark Theme')
        elif self.theme_state == "dark":
            self.theme_toggler.set_icon_name('light-theme')
            self.theme_toggler.set_tooltip('Switch to Light Theme')

        self.emit('theme-changed', self.theme_state)

    def set_font_size(self, font_size):
        self.font_size = font_size
        self.emit('font-size-changed', self.font_size)

    def _font_size_increase(self, button):
        self.font_size += FONT_CHANGE_STEP
        self.emit('font-size-changed', self.font_size)

    def _font_size_decrease(self, button):
        self.font_size -= FONT_CHANGE_STEP
        self.emit('font-size-changed', self.font_size)

    def _toggled_theme(self, button):
        if self.theme_state == "dark":
            self.theme_state = "light"
            self.theme_toggler.set_icon_name('dark-theme')
            self.theme_toggler.set_tooltip('Switch to Dark Theme')
        elif self.theme_state == "light":
            self.theme_state = "dark"
            self.theme_toggler.set_icon_name('light-theme')
            self.theme_toggler.set_tooltip('Switch to Light Theme')

        self.emit('theme-changed', self.theme_state)