Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManuel QuiƱones <manuq@laptop.org>2011-10-25 22:02:26 (GMT)
committer Gonzalo Odiard <godiard@gmail.com>2011-10-26 12:53:53 (GMT)
commit7944c353e8d6bc3ff5d235db8ffbd5763bf40d6b (patch)
tree8c93046e145ddb4502ea38adc55c28d5a15f75a2
parentfb4b389e0bb2334eac8268eed842161bc6ff8b41 (diff)
Playlist widget
This adds a playlist widget to Jukebox. The activity already has the ability to add several media and going back and forward through them. Now this information is shown in a list. Also, activating a row starts playing the corresponding media. Signed-off-by: Manuel QuiƱones <manuq@laptop.org>
-rw-r--r--ControlToolbar.py16
-rw-r--r--ViewToolbar.py71
-rw-r--r--jukeboxactivity.py107
-rw-r--r--widgets.py87
4 files changed, 174 insertions, 107 deletions
diff --git a/ControlToolbar.py b/ControlToolbar.py
index 8d63761..7ef8a5b 100644
--- a/ControlToolbar.py
+++ b/ControlToolbar.py
@@ -21,6 +21,7 @@ import gobject
import gtk
from sugar.graphics.toolbutton import ToolButton
+from sugar.graphics.toggletoolbutton import ToggleToolButton
class ViewToolbar(gtk.Toolbar):
@@ -29,7 +30,10 @@ class ViewToolbar(gtk.Toolbar):
__gsignals__ = {
'go-fullscreen': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
- ([]))
+ ([])),
+ 'toggle-playlist': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([]))
}
def __init__(self):
@@ -41,9 +45,19 @@ class ViewToolbar(gtk.Toolbar):
self.insert(self._fullscreen, -1)
self._fullscreen.show()
+ self._show_playlist = ToggleToolButton('view-list')
+ self._show_playlist.set_active(True)
+ self._show_playlist.set_tooltip(_('Show Playlist'))
+ self._show_playlist.connect('toggled', self._playlist_toggled_cb)
+ self.insert(self._show_playlist, -1)
+ self._show_playlist.show()
+
def _fullscreen_cb(self, button):
self.emit('go-fullscreen')
+ def _playlist_toggled_cb(self, button):
+ self.emit('toggle-playlist')
+
class Control(gobject.GObject):
"""Class to create the Control (play) toolbar"""
diff --git a/ViewToolbar.py b/ViewToolbar.py
deleted file mode 100644
index d884a5a..0000000
--- a/ViewToolbar.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright (C) 2008 Kushal Das <kushal@fedoraproject.org>
-#
-# 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
-
-from gettext import gettext as _
-
-import gobject
-import gtk
-
-from sugar.graphics.toolbutton import ToolButton
-
-
-class ViewToolbar(gtk.Toolbar):
- """Class to create the view toolbar"""
-
- __gsignals__ = {
- 'go-fullscreen': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([]))
- }
-
- def __init__(self, toolbox, jukebox):
- gtk.Toolbar.__init__(self)
- self.toolbox = toolbox
- self.jukebox = jukebox
-
- self._zoom_tofit = ToolButton('zoom-best-fit')
- self._zoom_tofit.set_tooltip(_('Fit to window'))
- self._zoom_tofit.connect('clicked', self._zoom_tofit_cb)
- self.insert(self._zoom_tofit, -1)
- self._zoom_tofit.show()
-
- self._zoom_original = ToolButton('zoom-original')
- self._zoom_original.set_tooltip(_('Original size'))
- self._zoom_original.connect('clicked', self._zoom_original_cb)
- self.insert(self._zoom_original, -1)
- self._zoom_original.show()
-
- spacer = gtk.SeparatorToolItem()
- spacer.props.draw = False
- self.insert(spacer, -1)
- spacer.show()
-
- self._fullscreen = ToolButton('view-fullscreen')
- self._fullscreen.set_tooltip(_('Fullscreen'))
- self._fullscreen.connect('clicked', self._fullscreen_cb)
- self.insert(self._fullscreen, -1)
- self._fullscreen.show()
-
- def _zoom_tofit_cb(self, button):
- pass
- #self.jukebox.player.set_fit_to_screen_cb()
-
- def _zoom_original_cb(self, button):
- pass
- #self.jukebox.player.set_original_to_size_cb()
-
- def _fullscreen_cb(self, button):
- self.emit('go-fullscreen')
diff --git a/jukeboxactivity.py b/jukeboxactivity.py
index 52195a9..4864a65 100644
--- a/jukeboxactivity.py
+++ b/jukeboxactivity.py
@@ -60,6 +60,10 @@ from ControlToolbar import Control, ViewToolbar
from ConfigParser import ConfigParser
cf = ConfigParser()
+from widgets import PlayListWidget
+
+PLAYLIST_WIDTH_PROP = 1.0 / 3
+
class JukeboxActivity(activity.Activity):
UPDATE_INTERVAL = 500
@@ -82,6 +86,8 @@ class JukeboxActivity(activity.Activity):
_view_toolbar = ViewToolbar()
_view_toolbar.connect('go-fullscreen',
self.__go_fullscreen_cb)
+ _view_toolbar.connect('toggle-playlist',
+ self.__toggle_playlist_cb)
toolbox.add_toolbar(_('View'), _view_toolbar)
_view_toolbar.show()
@@ -108,6 +114,8 @@ class JukeboxActivity(activity.Activity):
_view_toolbar = ViewToolbar()
_view_toolbar.connect('go-fullscreen',
self.__go_fullscreen_cb)
+ _view_toolbar.connect('toggle-playlist',
+ self.__toggle_playlist_cb)
view_toolbar_button = ToolbarButton(
page=_view_toolbar,
icon_name='toolbar-view')
@@ -142,7 +150,10 @@ class JukeboxActivity(activity.Activity):
self.seek_timeout_id = -1
self.player = None
self.uri = None
+
+ # {'url': 'file://.../media.ogg', 'title': 'My song'}
self.playlist = []
+
self.jobjectlist = []
self.playpath = None
self.currentplaying = None
@@ -155,12 +166,19 @@ class JukeboxActivity(activity.Activity):
self.p_duration = gst.CLOCK_TIME_NONE
self.bin = gtk.HBox()
+ self.bin.show()
+ self.playlist_widget = PlayListWidget(self.play)
+ self.playlist_widget.update(self.playlist)
+ self.playlist_widget.show()
+ self.bin.pack_start(self.playlist_widget, expand=False)
self._empty_widget = gtk.Label("")
self._empty_widget.show()
self.videowidget = VideoWidget()
self._switch_canvas(show_video=False)
self.set_canvas(self.bin)
self.show_all()
+ self.bin.connect('size-allocate', self.__size_allocate_cb)
+
#From ImageViewer Activity
self._want_document = True
if self._object_id is None:
@@ -169,7 +187,7 @@ class JukeboxActivity(activity.Activity):
if handle.uri:
self.uri = handle.uri
- gobject.idle_add(self._start, self.uri)
+ gobject.idle_add(self._start, self.uri, handle.title)
def _switch_canvas(self, show_video):
"""Show or hide the video visualization in the canvas.
@@ -180,12 +198,17 @@ class JukeboxActivity(activity.Activity):
"""
if show_video:
self.bin.remove(self._empty_widget)
- self.bin.add(self.videowidget)
+ self.bin.pack_end(self.videowidget)
else:
- self.bin.add(self._empty_widget)
+ self.bin.pack_end(self._empty_widget)
self.bin.remove(self.videowidget)
self.bin.queue_draw()
+ def __size_allocate_cb(self, widget, allocation):
+ canvas_size = self.bin.get_allocation()
+ playlist_width = int(canvas_size.width * PLAYLIST_WIDTH_PROP)
+ self.playlist_widget.set_size_request(playlist_width, 0)
+
def open_button_clicked_cb(self, widget):
""" To open the dialog to select a new file"""
#self.player.seek(0L)
@@ -219,38 +242,35 @@ class JukeboxActivity(activity.Activity):
# return
self.player.seek(0L)
if direction == "prev" and self.currentplaying > 0:
- self.currentplaying -= 1
- self.player.stop()
- self._switch_canvas(show_video=True)
- self.player = GstPlayer(self.videowidget)
- self.player.connect("error", self._player_error_cb)
- self.player.connect("tag", self._player_new_tag_cb)
- self.player.connect("stream-info", self._player_stream_info_cb)
- self.player.set_uri(self.playlist[self.currentplaying])
- logging.info("prev: " + self.playlist[self.currentplaying])
+ self.play(self.currentplaying - 1)
+ logging.info("prev: " + self.playlist[self.currentplaying]['url'])
#self.playflag = True
- self.play_toggled()
- self.player.connect("eos", self._player_eos_cb)
elif direction == "next" and \
self.currentplaying < len(self.playlist) - 1:
- self.currentplaying += 1
- self.player.stop()
- self._switch_canvas(show_video=True)
- self.player = GstPlayer(self.videowidget)
- self.player.connect("error", self._player_error_cb)
- self.player.connect("tag", self._player_new_tag_cb)
- self.player.connect("stream-info", self._player_stream_info_cb)
- self.player.set_uri(self.playlist[self.currentplaying])
- logging.info("NExt: " + self.playlist[self.currentplaying])
+ self.play(self.currentplaying + 1)
+ logging.info("next: " + self.playlist[self.currentplaying]['url'])
#self.playflag = True
- self.play_toggled()
- self.player.connect("eos", self._player_eos_cb)
else:
self.play_toggled()
self.player.stop()
self._switch_canvas(show_video=False)
self.player.set_uri(None)
+ self.check_if_next_prev()
+
+ def play(self, media_index):
+ self._switch_canvas(show_video=True)
+ self.currentplaying = media_index
+ self.player.stop()
+ self.player = GstPlayer(self.videowidget)
+ self.player.connect("eos", self._player_eos_cb)
+ self.player.connect("error", self._player_error_cb)
+ self.player.connect("tag", self._player_new_tag_cb)
+ self.player.connect("stream-info", self._player_stream_info_cb)
+ self.player.set_uri(self.playlist[self.currentplaying]['url'])
+
+ self.play_toggled()
self.check_if_next_prev()
+ self.playlist_widget.set_cursor(self.currentplaying)
def _player_eos_cb(self, widget):
self.songchange('next')
@@ -323,7 +343,8 @@ class JukeboxActivity(activity.Activity):
jobject = chooser.get_selected_object()
if jobject and jobject.file_path:
self.jobjectlist.append(jobject)
- self._start(jobject.file_path)
+ title = jobject.metadata.get('title', None)
+ self._start(jobject.file_path, title)
finally:
#chooser.destroy()
#del chooser
@@ -333,7 +354,8 @@ class JukeboxActivity(activity.Activity):
self.uri = os.path.abspath(file_path)
if os.path.islink(self.uri):
self.uri = os.path.realpath(self.uri)
- gobject.idle_add(self._start, self.uri)
+ title = self.metadata.get('title', None)
+ gobject.idle_add(self._start, self.uri, title)
def getplaylist(self, links):
result = []
@@ -347,28 +369,34 @@ class JukeboxActivity(activity.Activity):
urllib.quote(os.path.join(self.playpath, x)))
return result
- def _start(self, uri=None):
+ def _start(self, uri=None, title=None):
self._want_document = False
self.playpath = os.path.dirname(uri)
if not uri:
return False
# FIXME: parse m3u files and extract actual URL
if uri.endswith(".m3u") or uri.endswith(".m3u8"):
- self.playlist.extend(self.getplaylist([line.strip()
- for line in open(uri).readlines()]))
+ for line in open(uri).readlines():
+ url = line.strip()
+ self.playlist.extend({'url': url, 'title': title})
elif uri.endswith('.pls'):
try:
cf.readfp(open(uri))
x = 1
while True:
- self.playlist.append(cf.get("playlist", 'File' + str(x)))
+ url = cf.get("playlist", 'File' + str(x))
+ self.playlist.append({'url': url, 'title': title})
x += 1
except:
#read complete
pass
+
+ elif uri.startswith("file://"):
+ self.playlist.append({'url': uri, 'title': title})
+
else:
- self.playlist.append("file://" +
- urllib.quote(os.path.abspath(uri)))
+ url = "file://" + urllib.quote(os.path.abspath(uri))
+ self.playlist.append({'url': url, 'title': title})
if not self.player:
# lazy init the player so that videowidget is realized
# and has a valid widget allocation
@@ -379,10 +407,12 @@ class JukeboxActivity(activity.Activity):
self.player.connect("tag", self._player_new_tag_cb)
self.player.connect("stream-info", self._player_stream_info_cb)
+ self.playlist_widget.update(self.playlist)
+
try:
if not self.currentplaying:
- logging.info("Playing: " + self.playlist[0])
- self.player.set_uri(self.playlist[0])
+ logging.info("Playing: " + self.playlist[0]['url'])
+ self.player.set_uri(self.playlist[0]['url'])
self.currentplaying = 0
self.play_toggled()
self.show_all()
@@ -469,6 +499,13 @@ class JukeboxActivity(activity.Activity):
def __go_fullscreen_cb(self, toolbar):
self.fullscreen()
+ def __toggle_playlist_cb(self, toolbar):
+ if self.playlist_widget.get_visible():
+ self.playlist_widget.hide()
+ else:
+ self.playlist_widget.show_all()
+ self.bin.queue_draw()
+
class GstPlayer(gobject.GObject):
__gsignals__ = {
diff --git a/widgets.py b/widgets.py
new file mode 100644
index 0000000..67c5bc4
--- /dev/null
+++ b/widgets.py
@@ -0,0 +1,87 @@
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+import logging
+from gettext import gettext as _
+
+import pygtk
+pygtk.require('2.0')
+
+import gobject
+import gtk
+import pango
+
+
+COLUMNS_NAME = ('index', 'media')
+COLUMNS = dict((name, i) for i, name in enumerate(COLUMNS_NAME))
+
+
+class PlayListWidget(gtk.ScrolledWindow):
+ def __init__(self, play_callback):
+ self._playlist = None
+ self._play_callback = play_callback
+
+ gtk.ScrolledWindow.__init__(self, hadjustment=None,
+ vadjustment=None)
+ self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ self.listview = gtk.TreeView()
+ self.treemodel = gtk.ListStore(int, object)
+ self.listview.set_model(self.treemodel)
+ selection = self.listview.get_selection()
+ selection.set_mode(gtk.SELECTION_SINGLE)
+
+ renderer_idx = gtk.CellRendererText()
+ treecol_idx = gtk.TreeViewColumn(_('No.'))
+ treecol_idx.pack_start(renderer_idx, True)
+ treecol_idx.set_cell_data_func(renderer_idx, self._set_number)
+ self.listview.append_column(treecol_idx)
+
+ renderer_title = gtk.CellRendererText()
+ renderer_title.set_property('ellipsize', pango.ELLIPSIZE_END)
+ treecol_title = gtk.TreeViewColumn(_('Play List'))
+ treecol_title.pack_start(renderer_title, True)
+ treecol_title.set_cell_data_func(renderer_title, self._set_title)
+ self.listview.append_column(treecol_title)
+
+ # we don't support search in the playlist for the moment:
+ self.listview.set_enable_search(False)
+
+ self.listview.connect('row-activated', self.__on_row_activated)
+
+ self.add(self.listview)
+
+ def __on_row_activated(self, treeview, path, col):
+ model = treeview.get_model()
+ media_idx = path[COLUMNS['index']]
+ self._play_callback(media_idx)
+
+ def _set_number(self, column, cell, model, it):
+ idx = model.get_value(it, COLUMNS['index'])
+ cell.set_property('text', idx + 1)
+
+ def _set_title(self, column, cell, model, it):
+ playlist_item = model.get_value(it, COLUMNS['media'])
+ cell.set_property('text', playlist_item['title'])
+
+ def update(self, playlist):
+ self.treemodel.clear()
+ self._playlist = playlist
+ pl = list(enumerate(playlist))
+ for i, media in pl:
+ self.treemodel.append((i, media))
+ self.set_cursor(0)
+
+ def set_cursor(self, index):
+ self.listview.set_cursor((index,))