# Copyright 2009 Simon Schampijer
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# gst-launch-0.10 playbin2 uri=file:///.....
import os
import math
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GdkPixbuf
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import logging
from gettext import gettext as _
from sugar3.activity import activity
from sugar3.graphics.toolbarbox import ToolbarBox
from sugar3.graphics.toolbutton import ToolButton
from sugar3.activity.widgets import ActivityToolbarButton
from sugar3.activity.widgets import StopButton
from sugar3.graphics import style
from sugar3 import profile
from sugar3.graphics.icon import EventIcon
from roundbox import RoundBox
from data import DAT_DIC
MAP_PAGE = 0
VIDEO_PAGE = 1
INFO_PAGE = 2
class MyEcoTvActivity(activity.Activity):
def __init__(self, handle):
activity.Activity.__init__(self, handle)
# we do not have collaboration features
self.max_participants = 1
toolbar_box = ToolbarBox()
activity_button = ActivityToolbarButton(self)
toolbar_box.toolbar.insert(activity_button, 0)
toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
self._world_bt = ToolButton('world-map-au')
self._world_bt.set_tooltip(_('Map'))
self._world_bt.connect('clicked',
self.__show_world_map_cb)
toolbar_box.toolbar.insert(self._world_bt, -1)
self._play_bt = ToolButton('player_play')
self._play_bt.set_tooltip(_('Video'))
self._play_bt.connect('clicked',
self.__play_video_cb)
toolbar_box.toolbar.insert(self._play_bt, -1)
toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
self._text_level = 'text_easy'
self._text_easy_bt = ToolButton('easy')
self._text_easy_bt.set_tooltip(_('Easy text'))
self._text_easy_bt.connect('clicked',
self.__show_info_cb, 'text_easy')
toolbar_box.toolbar.insert(self._text_easy_bt, -1)
self._text_medium_bt = ToolButton('medium')
self._text_medium_bt.set_tooltip(_('Medium text'))
self._text_medium_bt.connect('clicked',
self.__show_info_cb, 'text_medium')
toolbar_box.toolbar.insert(self._text_medium_bt, -1)
self._text_hard_bt = ToolButton('hard')
self._text_hard_bt.set_tooltip(_('Hard text'))
self._text_hard_bt.connect('clicked',
self.__show_info_cb, 'text_hard')
toolbar_box.toolbar.insert(self._text_hard_bt, -1)
separator = Gtk.SeparatorToolItem()
separator.props.draw = False
separator.set_expand(True)
toolbar_box.toolbar.insert(separator, -1)
stop_button = StopButton(self)
toolbar_box.toolbar.insert(stop_button, -1)
self.set_toolbar_box(toolbar_box)
toolbar_box.show_all()
self._notebook = Gtk.Notebook()
self._notebook.set_show_tabs(False)
worldmap = WorldMap()
worldmap.connect('icon-clicked', self.__icon_clicked_cb)
self._notebook.append_page(worldmap, None)
self._videoplayer = VideoPlayer()
self._notebook.append_page(self._videoplayer, None)
self._notebook.show_all()
self.set_canvas(self._notebook)
self._icon_selected = None
self.set_current_page(MAP_PAGE)
def get_data_icon(self, icon_name):
for data in DAT_DIC:
if icon_name == data['name']:
return data
return None
def __icon_clicked_cb(self, worldmap, icon_name):
logging.error('Icon %s clicked', icon_name)
self._icon_selected = icon_name
data = self.get_data_icon(icon_name)
logging.error(data)
if data is not None and data['video'] is not None:
self.show_video(data['video'])
def show_video(self, video):
# the information box resizes the notebook
# need remove it to have the video desplayed in
# the correct pvertical position
if self._notebook.get_n_pages() > 2:
self._notebook.remove_page(-1)
self.set_current_page(VIDEO_PAGE)
video_path = os.path.join(activity.get_bundle_path(), 'video',
video)
self._videoplayer.play(video_path)
def __show_world_map_cb(self, button):
self._icon_selected = None
self.set_current_page(MAP_PAGE)
def __play_video_cb(self, button):
data = self.get_data_icon(self._icon_selected)
if data is not None and data['video'] is not None:
self.show_video(data['video'])
def __show_info_cb(self, button, text_level):
self._text_level = text_level
if self._icon_selected is None:
return
data = self.get_data_icon(self._icon_selected)
if data is not None:
if self._notebook.get_n_pages() > 2:
self._notebook.remove_page(-1)
info_box = InformationBox(data['icon_name'], _('Did you know?'),
data[text_level])
self._notebook.append_page(info_box, None)
self.set_current_page(INFO_PAGE)
def write_file(self, file_name):
pass
def read_file(self, file_name):
pass
def set_current_page(self, page):
self._notebook.set_current_page(page)
self._world_bt.set_sensitive(page != MAP_PAGE)
self._text_easy_bt.set_sensitive(page in (VIDEO_PAGE, INFO_PAGE))
self._text_medium_bt.set_sensitive(page in (VIDEO_PAGE, INFO_PAGE))
self._text_hard_bt.set_sensitive(page in (VIDEO_PAGE, INFO_PAGE))
self._play_bt.set_sensitive(page == INFO_PAGE)
if page != VIDEO_PAGE:
self._videoplayer.stop()
class VideoPlayer(Gtk.EventBox):
def __init__(self):
super(VideoPlayer, self).__init__()
self._width = Gdk.Screen.width()
self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE
self.set_size_request(self._width, self._height)
self.set_double_buffered(False)
self.set_app_paintable(True)
self._sink = None
self._xid = None
self.connect('realize', self.__realize)
self.playing = False
self.paused = False
self._vpipeline = gst.element_factory_make('playbin2', 'vplayer')
bus = self._vpipeline.get_bus()
bus.enable_sync_message_emission()
bus.add_signal_watch()
bus.connect('sync-message::element', self.__on_sync_message)
bus.connect('message', self.__on_vmessage)
def __on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
message.src.set_property('force-aspect-ratio', True)
self._sink = message.src
self._sink.set_xwindow_id(self._xid)
def __on_vmessage(self, bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
self._vpipeline.seek_simple(
gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, 0)
self.playing = False
def __realize(self, widget):
self._xid = self.get_window().get_xid()
def do_expose_event(self):
if self._sink:
self._sink.expose()
return False
else:
return True
def play(self, filename):
if filename:
path = os.path.join(
activity.get_bundle_path(), 'video', filename)
self._vpipeline.set_property('uri', "file://" + path)
self._vpipeline.set_state(gst.STATE_PLAYING)
self.playing = True
def stop(self):
self._vpipeline.set_state(gst.STATE_NULL)
self.playing = False
def pause(self):
self._vpipeline.set_state(gst.STATE_PAUSED)
self.paused = True
def unpause(self):
self._vpipeline.set_state(gst.STATE_PLAYING)
self.paused = False
class InformationBox(Gtk.EventBox):
def __init__(self, icon_name, title, text):
"""
icon_name: str
title: str
text: array of str
"""
Gtk.EventBox.__init__(self)
self._width = Gdk.Screen.width()
self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE
self.set_size_request(self._width, self._height)
self.modify_bg(Gtk.StateType.NORMAL,
style.COLOR_WHITE.get_gdk_color())
self._icon_name = icon_name
roundbox = RoundBox()
roundbox.background_color = style.Color('#179820')
roundbox.border_color = style.Color('#179820')
roundbox.props.margin = style.GRID_CELL_SIZE
vbox = Gtk.VBox()
vbox.props.margin = style.GRID_CELL_SIZE * 1.5
if style.zoom(100) == 100:
# xo
self._font_size = 12
text_changer_vertical = 3
else:
# desktop
self._font_size = 25
text_changer_vertical = 3.5
self._order = 0
self._text = text
title_label = Gtk.Label()
title_label.set_markup(
'%s\n' %
(self._font_size, title))
title_label.set_alignment(0.0, 0.0)
vbox.pack_start(title_label, False, False, 10)
self.text_label = Gtk.Label()
self.text_label.set_markup(
'%s' %
(self._font_size, text[self._order]))
self.text_label.set_alignment(0.0, 0.5)
self.text_label.set_line_wrap(True)
vbox.pack_start(self.text_label, False, False, 10)
roundbox.add(vbox)
self._xo_color = profile.get_color()
overlay = Gtk.Overlay()
self.add(overlay)
overlay.add(roundbox)
if len(text) > 0:
text_changer = TextChanger(self._xo_color, len(text))
text_changer.connect('change-text', self.__change_text)
text_changer.props.halign = Gtk.Align.CENTER
text_changer.props.valign = Gtk.Align.START
text_changer.props.margin_top = self._height - \
(style.GRID_CELL_SIZE * text_changer_vertical)
overlay.add_overlay(text_changer)
icon = EventIcon(icon_name='globe', xo_color=self._xo_color)
icon.props.halign = Gtk.Align.START
icon.props.valign = Gtk.Align.START
icon.props.margin_top = 0
icon.props.margin_left = int(style.GRID_CELL_SIZE * 2.6)
overlay.add_overlay(icon)
icon.set_size(style.zoom(266))
icon = EventIcon(icon_name=self._icon_name, xo_color=self._xo_color)
icon.props.halign = Gtk.Align.START
icon.props.valign = Gtk.Align.START
icon.props.margin_top = style.GRID_CELL_SIZE / 2
icon.props.margin_left = style.GRID_CELL_SIZE * 3
overlay.add_overlay(icon)
icon.set_size(style.zoom(200))
self.show_all()
def __change_text(self, widget, order):
self._order = order
self.text_label.set_markup(
'%s' %
(self._font_size, self._text[self._order]))
class TextChanger(Gtk.HBox):
__gsignals__ = {
'change-text': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE,
([int])), }
def __init__(self, xo_color, array_size):
Gtk.HBox.__init__(self)
self._xo_color = xo_color
self._array_size = array_size
self._order = 0
prev_bt = EventIcon(icon_name='go-previous-paired-grey',
xo_color=self._xo_color)
prev_bt.connect('button-press-event', self.__prev_clicked_cb)
self.pack_start(prev_bt, False, False, 5)
self.sequence_view = SequenceView(array_size)
self.pack_start(self.sequence_view, False, False, 5)
next_bt = EventIcon(icon_name='go-next-paired-grey',
xo_color=self._xo_color)
next_bt.connect('button-press-event', self.__next_clicked_cb)
self.pack_start(next_bt, False, False, 5)
def __prev_clicked_cb(self, icon, event):
if self._order == 0:
self._order = self._array_size - 1
else:
self._order = self._order - 1
self.sequence_view.set_value(self._order)
self.emit('change-text', self._order)
def __next_clicked_cb(self, icon, event):
if self._order < self._array_size - 1:
self._order = self._order + 1
else:
self._order = 0
self.sequence_view.set_value(self._order)
self.emit('change-text', self._order)
class SequenceView(Gtk.DrawingArea):
def __init__(self, cant, point_size=10):
Gtk.DrawingArea.__init__(self)
self._cant = cant
self._value = 0
self._point_size = point_size
logging.error('init SequenceView cant= %d', self._cant)
self.set_size_request(self._point_size * self._cant * 2,
self._point_size)
self.connect('draw', self.__draw_cb)
def size_allocate_cb(widget, allocation):
self._width, self._height = allocation.width, allocation.height
self.disconnect(self._setup_handle)
self._setup_handle = self.connect('size_allocate',
size_allocate_cb)
def set_value(self, value):
self._value = value
self.queue_draw()
def __draw_cb(self, widget, ctx):
if self._cant == 0:
return
ctx.save()
radio = self._point_size / 2.0
ctx.translate(0, 0)
#ctx.rectangle(0, 0, self._width, self._height)
#ctx.set_source_rgb(1.0, 1.0, 1.0)
#ctx.fill()
ctx.translate(radio, self._height / 2 - radio)
for n in range(self._cant):
if n == self._value:
ctx.set_source_rgb(0.913, 0.733, 0.0) # eebb00
else:
ctx.set_source_rgb(0.33, 0.33, 0.33) # grey
ctx.arc(radio, radio, radio, 0., 2 * math.pi)
ctx.fill()
ctx.translate(self._point_size * 2, 0)
ctx.restore()
class WorldMap(Gtk.EventBox):
__gsignals__ = {
'icon-clicked': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE,
([str])), }
def __init__(self):
Gtk.EventBox.__init__(self)
self._width = Gdk.Screen.width()
self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE
self.set_size_request(self._width, self._height)
self._background_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
'./MAP.svg', self._width, self._height)
self._xo_color = profile.get_color()
self._fixed = Gtk.Fixed()
self.connect('draw', self.__draw_cb)
for data in DAT_DIC:
icon = EventIcon(icon_name=data['icon_name'],
xo_color=self._xo_color)
icon.set_size(100)
x = int((data['icon_position'][0] + 1) * self._width / 2)
y = int((1 - data['icon_position'][1]) * self._height / 2)
self._fixed.put(icon, x, y)
icon.show()
icon.connect('button-press-event',
self.__icon_button_press_cb, data['name'])
self._fixed.show()
self.add(self._fixed)
def __draw_cb(self, widget, ctx):
ctx.rectangle(0, 0, self._width, self._height)
ctx.set_source_rgba(1.0, 1.0, 1.0, 1.0)
ctx.paint()
ctx.save()
ctx.translate(0, 0)
scale_x = float(self._width) / self._background_pixbuf.get_width()
scale_y = float(self._height) / self._background_pixbuf.get_height()
ctx.scale(scale_x, scale_y)
Gdk.cairo_set_source_pixbuf(ctx, self._background_pixbuf, 0, 0)
ctx.paint()
ctx.restore()
def __icon_button_press_cb(self, icon, event, data_name):
self.emit('icon-clicked', data_name)