Devs, I've pushed some modifications after Paul's comments. new diff : === added file 'cfg/themes/Black/images/volume.png' Binary files cfg/themes/Black/images/volume.png 1970-01-01 00:00:00 +0000 and cfg/themes/Black/images/volume.png 2009-06-27 09:25:00 +0000 differ === added file 'cfg/themes/Black/images/volume_filled.png' Binary files cfg/themes/Black/images/volume_filled.png 1970-01-01 00:00:00 +0000 and cfg/themes/Black/images/volume_filled.png 2009-06-01 20:55:32 +0000 differ === added file 'cfg/themes/Black/images/volume_unfilled.png' Binary files cfg/themes/Black/images/volume_unfilled.png 1970-01-01 00:00:00 +0000 and cfg/themes/Black/images/volume_unfilled.png 2009-06-01 20:55:34 +0000 differ === added file 'cfg/themes/Default/images/volume.png' Binary files cfg/themes/Default/images/volume.png 1970-01-01 00:00:00 +0000 and cfg/themes/Default/images/volume.png 2009-06-01 20:55:33 +0000 differ === added file 'cfg/themes/Default/images/volume_filled.png' Binary files cfg/themes/Default/images/volume_filled.png 1970-01-01 00:00:00 +0000 and cfg/themes/Default/images/volume_filled.png 2009-06-01 20:55:34 +0000 differ === added file 'cfg/themes/Default/images/volume_unfilled.png' Binary files cfg/themes/Default/images/volume_unfilled.png 1970-01-01 00:00:00 +0000 and cfg/themes/Default/images/volume_unfilled.png 2009-06-01 20:55:34 +0000 differ === modified file 'entertainerlib/frontend/gui/user_event.py' --- entertainerlib/frontend/gui/user_event.py 2009-05-06 03:40:22 +0000 +++ entertainerlib/frontend/gui/user_event.py 2009-06-01 20:55:33 +0000 @@ -18,6 +18,8 @@ PLAYER_PREVIOUS = 3 PLAYER_SKIP_FORWARD = 4 PLAYER_SKIP_BACKWARD = 5 + PLAYER_VOLUME_UP = 6 + PLAYER_VOLUME_DOWN = 7 # Navigation events NAVIGATE_UP = 20 === modified file 'entertainerlib/frontend/gui/user_interface.py' --- entertainerlib/frontend/gui/user_interface.py 2009-06-28 20:12:13 +0000 +++ entertainerlib/frontend/gui/user_interface.py 2009-06-29 19:35:06 +0000 @@ -20,6 +20,7 @@ from entertainerlib.frontend.gui.user_event import UserEvent from entertainerlib.frontend.gui.widgets.menu_overlay import MenuOverlay from entertainerlib.frontend.media_player import MediaPlayer +from entertainerlib.frontend.gui.widgets.volume_indicator import VolumeIndicator from entertainerlib.utils.configuration import Configuration from entertainerlib.utils.logger import Logger @@ -92,6 +93,7 @@ self.player = MediaPlayer(self.stage, self.config.get_stage_width(), self.config.get_stage_height()) + self.player.connect('volume-changed', self._on_volume_changed) # Initialize menu overlay texture self.is_overlay = False @@ -101,6 +103,15 @@ self.config.get_stage_width(), self.config.get_stage_height()) self.stage.add(self.menu_overlay) + self.volume_indicator = VolumeIndicator() + self.stage.add(self.volume_indicator) + self.volume_indicator.connect('hiding', + self._on_volume_indicator_hiding) + self.fade_screen_timeline = clutter.Timeline(13, 26) + alpha = clutter.Alpha(self.fade_screen_timeline, + clutter.smoothstep_inc_func) + self.fade_screen_behaviour = clutter.BehaviourOpacity(255, 0, alpha) + # Transition object. Handles effects between screen changes. transition_factory = TransitionFactory(self._remove_from_stage) self.transition = transition_factory.generate_transition() @@ -139,6 +150,8 @@ clutter.keysyms.c : UserEvent.PLAYER_SKIP_FORWARD, clutter.keysyms.z : UserEvent.PLAYER_PREVIOUS, clutter.keysyms.v : UserEvent.PLAYER_NEXT, + clutter.keysyms.m : UserEvent.PLAYER_VOLUME_UP, + clutter.keysyms.l : UserEvent.PLAYER_VOLUME_DOWN, clutter.keysyms.q : UserEvent.QUIT_FRONTEND, clutter.keysyms.Escape : UserEvent.QUIT_FRONTEND }) @@ -167,6 +180,8 @@ UserEvent.PLAYER_SKIP_FORWARD : self._handle_player_skip_forward, UserEvent.PLAYER_PREVIOUS : self._handle_player_previous, UserEvent.PLAYER_NEXT : self._handle_player_next, + UserEvent.PLAYER_VOLUME_UP : self._handle_player_volume_up, + UserEvent.PLAYER_VOLUME_DOWN : self._handle_player_volume_down, UserEvent.QUIT_FRONTEND : self._handle_quit_frontend } @@ -384,6 +399,14 @@ self.player.stop() self.current.handle_user_event(event) + def _handle_player_volume_up(self, event): + '''Handle UserEvent.PLAYER_VOLUME_UP.''' + self.player.volume_up() + + def _handle_player_volume_down(self, event): + '''Handle UserEvent.PLAYER_VOLUME_DOWN.''' + self.player.volume_down() + def _handle_toggle_fullscreen(self, event): '''Handle UserEvent.TOGGLE_FULLSCREEN.''' self._toggle_fullscreen() @@ -392,3 +415,18 @@ '''Handle UserEvent.QUIT_FRONTEND.''' self.confirm_exit() + def _on_volume_changed(self, event): + '''Show volume indicator and fade out the screen (if needed).''' + if not self.volume_indicator.visible: + if not self.fade_screen_behaviour.is_applied(self.current): + self.fade_screen_behaviour.apply(self.current) + self.fade_screen_behaviour.set_bounds(255, 50) + self.fade_screen_timeline.start() + + self.volume_indicator.show_volume(self.player.volume) + + def _on_volume_indicator_hiding(self, event): + '''Restore previous screen opacity.''' + self.fade_screen_behaviour.set_bounds(50, 255) + self.fade_screen_timeline.start() + === added file 'entertainerlib/frontend/gui/widgets/volume_indicator.py' --- entertainerlib/frontend/gui/widgets/volume_indicator.py 1970-01-01 00:00:00 +0000 +++ entertainerlib/frontend/gui/widgets/volume_indicator.py 2009-06-29 19:05:34 +0000 @@ -0,0 +1,80 @@ +# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 +"""A volume indicator widgets.""" + +import clutter +import gobject + +from entertainerlib.frontend.gui.widgets.base import Base + +class VolumeIndicator(Base, clutter.Group): + """Volume Indicator displaying player's volume level.""" + __gsignals__ = { + 'hiding' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), + } + + def __init__(self): + Base.__init__(self) + clutter.Group.__init__(self) + + theme = self.config.theme + filled = clutter.Texture(theme.getImage("volume_filled")) + unfilled = clutter.Texture(theme.getImage("volume_unfilled")) + volume = clutter.Texture(theme.getImage("volume")) + self.add(volume) + + self._pokes = [] + + poke_width = filled.get_width() + + for i in range(20): + poke_filled = clutter.CloneTexture(filled) + poke_unfilled = clutter.CloneTexture(unfilled) + poke_filled.set_position(volume.get_width() + i * poke_width, 0) + poke_unfilled.set_position(volume.get_width() + i * poke_width, 0) + self.add(poke_filled) + self.add(poke_unfilled) + self._pokes.append([poke_filled, poke_unfilled]) + + self._hide_timeout_key = None + self.visible = False + self.set_opacity(0) + + self.timeline = clutter.Timeline(13, 26) + self.alpha = clutter.Alpha(self.timeline, clutter.smoothstep_inc_func) + self.behaviour = clutter.BehaviourOpacity(255, 0, self.alpha) + self.behaviour.apply(self) + + self.set_position(self.get_abs_x(0.35), self.get_abs_y(0.1)) + + def show_volume(self, volume): + """Displays volume level using filled and unfilled pokes.""" + self.raise_top() + + if self._hide_timeout_key is not None: + gobject.source_remove(self._hide_timeout_key) + self._hide_timeout_key = gobject.timeout_add(2000, + self.animate_out) + + for index, pokes in enumerate(self._pokes): + if index >= volume: + pokes[0].set_opacity(0) + pokes[1].set_opacity(255) + else: + pokes[0].set_opacity(255) + pokes[1].set_opacity(0) + + if self.visible == True: + return + + self.visible = True + self.behaviour.set_bounds(0, 255) + self.timeline.start() + + def animate_out(self): + """Fades out.""" + self.behaviour.set_bounds(255, 0) + self.timeline.start() + self.visible = False + self.emit("hiding") + return False + === modified file 'entertainerlib/frontend/media_player.py' --- entertainerlib/frontend/media_player.py 2009-06-28 20:12:13 +0000 +++ entertainerlib/frontend/media_player.py 2009-06-29 19:38:31 +0000 @@ -32,6 +32,7 @@ 'stop' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), 'skip-forward' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), 'skip-backward' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), + 'volume_changed' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), 'position-changed' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), 'refresh' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), } @@ -77,6 +78,8 @@ self.video_texture = cluttergst.VideoTexture() self.playbin = self.video_texture.get_playbin() + self.playbin.set_property("volume", 0.5) + self._volume = 10 self.bus = self.playbin.get_bus() self.bus.add_signal_watch() self.bus.connect('message', self._on_gst_message) @@ -107,6 +110,30 @@ self.logger.error("Error: %(err)s, %(debug)s" % \ {'err': err, 'debug': debug}) + def _get_volume(self): + """volume property getter.""" + return self._volume + + def _set_volume(self, volume): + """volume property setter.""" + self._volume = volume + if self._volume > 20: + self._volume = 20 + if self._volume < 0: + self._volume = 0 + self.playbin.set_property("volume", self._volume / 20.0) + self.emit('volume-changed') + + volume = property(_get_volume, _set_volume) + + def volume_up(self): + """Increase player's volume level.""" + self.volume = self._volume + 1 + + def volume_down(self): + """Decrease player's volume level.""" + self.volume = self._volume - 1 + def set_playlist(self, playlist): '''Set new playlist to MediaPlayer.''' if len(playlist) == 0: === added file 'entertainerlib/tests/test_mediaplayer.py' --- entertainerlib/tests/test_mediaplayer.py 1970-01-01 00:00:00 +0000 +++ entertainerlib/tests/test_mediaplayer.py 2009-06-29 19:29:57 +0000 @@ -0,0 +1,84 @@ +# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 +"""Tests MediaPlayer""" + +import clutter +import os + +from entertainerlib.frontend.media_player import MediaPlayer +from entertainerlib.frontend.medialibrary.videos import VideoItem +from entertainerlib.tests import EntertainerTest + +THIS_DIR = os.path.dirname(__file__) + +class MediaPlayerTest(EntertainerTest): + """Test for entertainerlib.frontend.gui.widgets.volume_indicator""" + + def setUp(self): + '''Set up the test.''' + EntertainerTest.setUp(self) + + self.player = MediaPlayer(clutter.Stage(), 100, 100) + self.video_item = VideoItem() + self.video_item.set_filename( + THIS_DIR + '/data/VideoThumbnailer/test.avi') + self.player.set_media(self.video_item) + + def test_create(self): + '''Test correct MediaPlayer initialization.''' + self.assertTrue(isinstance(self.player, MediaPlayer)) + + def test_volume(self): + '''Test the use of the volume property.''' + self.player.volume = 10 + self.assertEqual(self.player.volume, 10) + self.player.volume = 99 + self.assertEqual(self.player.volume, 20) + self.player.volume = -10 + self.assertEqual(self.player.volume, 0) + + def test_volumedown(self): + '''Test the use of the volume_down method.''' + self.player.volume = 10 + self.player.volume_down() + self.assertEqual(self.player.volume, 9) + + def test_volumeup(self): + '''Test the use of the volume_up method.''' + self.player.volume = 10 + self.player.volume_up() + self.assertEqual(self.player.volume, 11) + + def test_setmedia(self): + '''Test the use of the set_media method.''' + # The method is called during setUp. + self.assertTrue(self.player.media is not None) + + def test_getmedia(self): + '''Test the use of the get_media method.''' + self.assertEqual(self.player.get_media(), self.video_item) + + def test_hasmedia(self): + '''Test the use of the has_media method.''' + self.assertTrue(self.player.has_media()) + + def test_getmediatype(self): + '''Test the use of the get_media_type method.''' + self.assertEqual(self.player.get_media_type(), + self.video_item.get_type()) + + def test_playstop(self): + '''Test the use of the play and stop methods.''' + self.player.play() + self.assertTrue(self.player.is_playing) + self.player.stop() + self.assertFalse(self.player.is_playing) + + def test_getmediatitle(self): + '''Test the use of the get_media_title method.''' + self.assertEqual(self.player.get_media_title(), + THIS_DIR + '/data/VideoThumbnailer/test.avi') + + def test_getmediadurationstring(self): + '''Test the use of the get_media_title method.''' + self.assertEqual(self.player.get_media_duration_string(), "00:00") + === added file 'entertainerlib/tests/test_volumeindicator.py' --- entertainerlib/tests/test_volumeindicator.py 1970-01-01 00:00:00 +0000 +++ entertainerlib/tests/test_volumeindicator.py 2009-06-28 09:10:11 +0000 @@ -0,0 +1,24 @@ +# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 +"""Tests VolumeIndicator""" + +from entertainerlib.frontend.gui.widgets.volume_indicator import VolumeIndicator +from entertainerlib.tests import EntertainerTest + +class VolumeIndicatorTest(EntertainerTest): + """Test for entertainerlib.frontend.gui.widgets.volume_indicator""" + + def setUp(self): + '''Set up the test.''' + EntertainerTest.setUp(self) + + self.indicator = VolumeIndicator() + + def test_create(self): + '''Test correct VolumeIndicator initialization.''' + self.assertTrue(isinstance(self.indicator, VolumeIndicator)) + + def test_show_volume(self): + '''Test the use of the show_volume method.''' + self.indicator.show_volume(5) + self.assertTrue(self.indicator.visible) +