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()
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)
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)
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)
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
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
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()
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)
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
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
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
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
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()
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
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
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)
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
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)
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}"
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)
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
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)