示例#1
0
 def __init__(self, theme):
     """Initialize overlay texture."""
     Texture.__init__(self, filename = theme.getImage("menu_overlay"))
     self.timeline = clutter.Timeline(500)
     self.alpha = clutter.Alpha(self.timeline, clutter.EASE_IN_OUT_SINE)
     self.behaviour = clutter.BehaviourOpacity(255, 0, self.alpha)
     self.behaviour.apply(self)
示例#2
0
class TextureTest(EntertainerTest):
    """Test for entertainerlib.gui.widgets.texture"""
    def setUp(self):
        """Set up the test"""
        EntertainerTest.setUp(self)

        self.filename = os.path.join(THIS_DIR,
                                     'data/ImageThumbnailer/test.jpg')

        self.texture = Texture(self.filename, 0.1, 0.2)

    def tearDown(self):
        """Clean up after the test"""
        EntertainerTest.tearDown(self)

    def testCreate(self):
        """Test correct Texture initialization"""
        self.assertTrue(isinstance(self.texture, (Base, clutter.Texture)))

    def testPosition(self):
        """Test the position property"""
        self.assertEqual(self.texture.position, (0.1, 0.2))

        self.texture.position = (0.4, 0.5)
        self.assertEqual(self.texture.position, (0.4, 0.5))

        self.texture._set_position((0.3, 0.6))
        self.assertEqual(self.texture._get_position(), (0.3, 0.6))

    def testNoInitPosition(self):
        """Test that a texture will be displayed at the origin of the display
        area if no initial position is given"""
        no_pos_texture = Texture(self.filename)
        self.assertEqual(no_pos_texture.position, (0, 0))
示例#3
0
class ImageMenuItemTest(EntertainerTest):
    """Test for entertainerlib.gui.widgets.image_menu_item"""
    def setUp(self):
        '''Set up the test.'''
        EntertainerTest.setUp(self)

        self.filename = os.path.join(THIS_DIR,
                                     'data/ImageThumbnailer/test.jpg')
        self.texture = Texture(self.filename)
        self.original_ratio = float(self.texture.get_width()) / \
            self.texture.get_height()

        self.item = ImageMenuItem(0.2, 0.1, self.texture)

    def test_create(self):
        '''Test correct ImageMenuItem initialization.'''
        self.assertTrue(isinstance(self.item, ImageMenuItem))

    def test_width(self):
        '''Test correct width setting.'''
        self.assertTrue(self.texture.get_width() <= self.item.get_abs_x(0.2))

    def test_height(self):
        '''Test correct height setting.'''
        self.assertTrue(self.texture.get_height() <= self.item.get_abs_y(0.1))

    def test_ratio(self):
        '''Test that ratio correctly calculates the expected heights.'''
        self.assertTrue(self.original_ratio > self.item.original_ratio * 0.98)
        self.assertTrue(self.original_ratio < self.item.original_ratio * 1.02)
示例#4
0
 def __init__(self, theme):
     """Initialize overlay texture."""
     Texture.__init__(self, filename=theme.getImage("menu_overlay"))
     self.timeline = clutter.Timeline(500)
     self.alpha = clutter.Alpha(self.timeline, clutter.EASE_IN_OUT_SINE)
     self.behaviour = clutter.BehaviourOpacity(255, 0, self.alpha)
     self.behaviour.apply(self)
示例#5
0
class TextureTest(EntertainerTest):
    """Test for entertainerlib.gui.widgets.texture"""

    def setUp(self):
        """Set up the test"""
        EntertainerTest.setUp(self)

        self.filename = os.path.join(THIS_DIR, 'data/ImageThumbnailer/test.jpg')

        self.texture = Texture(self.filename, 0.1, 0.2)

    def tearDown(self):
        """Clean up after the test"""
        EntertainerTest.tearDown(self)

    def testCreate(self):
        """Test correct Texture initialization"""
        self.assertTrue(isinstance(self.texture, (Base, clutter.Texture)))

    def testPosition(self):
        """Test the position property"""
        self.assertEqual(self.texture.position, (0.1, 0.2))

        self.texture.position = (0.4, 0.5)
        self.assertEqual(self.texture.position, (0.4, 0.5))

        self.texture._set_position((0.3, 0.6))
        self.assertEqual(self.texture._get_position(), (0.3, 0.6))

    def testNoInitPosition(self):
        """Test that a texture will be displayed at the origin of the display
        area if no initial position is given"""
        no_pos_texture = Texture(self.filename)
        self.assertEqual(no_pos_texture.position, (0, 0))
示例#6
0
    def __init__(self, theme):
        clutter.Group.__init__(self)
        self.animate_selector = False

        selector_filename = theme.getImage("selector")
        glow_filename = theme.getImage("selector_glow")

        # Set selector base texture
        self.selector = Texture(selector_filename)
        self.selector.set_opacity(200)
        self.add(self.selector)

        # Set selector GLOW texture
        self.glow = Texture(glow_filename)
        self.glow.set_opacity(0)
        self.add(self.glow)

        # Animate selector (Glow effect with glow overlay texture)
        self.in_time = clutter.Timeline(1500)
        self.in_alpha = clutter.Alpha(self.in_time, clutter.EASE_IN_OUT_SINE)
        self.in_behaviour = clutter.BehaviourOpacity(0, 255, self.in_alpha)
        self.in_behaviour.apply(self.glow)

        self.out_time = clutter.Timeline(1500)
        self.out_alpha = clutter.Alpha(self.out_time, clutter.EASE_IN_OUT_SINE)
        self.out_behaviour = clutter.BehaviourOpacity(255, 0, self.out_alpha)
        self.out_behaviour.apply(self.glow)

        self.score = clutter.Score()
        self.score.set_loop(True)
        self.score.append(timeline=self.in_time)
        # Link the out Timeline so that there is a smooth fade out.
        self.score.append(timeline=self.out_time, parent=self.in_time)
class ImageMenuItemTest(EntertainerTest):
    """Test for entertainerlib.gui.widgets.image_menu_item"""

    def setUp(self):
        '''Set up the test.'''
        EntertainerTest.setUp(self)

        self.filename = os.path.join(THIS_DIR, 'data/ImageThumbnailer/test.jpg')
        self.texture = Texture(self.filename)
        self.original_ratio = float(self.texture.get_width()) / \
            self.texture.get_height()

        self.item = ImageMenuItem(0.2, 0.1, self.texture)

    def test_create(self):
        '''Test correct ImageMenuItem initialization.'''
        self.assertTrue(isinstance(self.item, ImageMenuItem))

    def test_width(self):
        '''Test correct width setting.'''
        self.assertTrue(self.texture.get_width() <= self.item.get_abs_x(0.2))

    def test_height(self):
        '''Test correct height setting.'''
        self.assertTrue(self.texture.get_height() <= self.item.get_abs_y(0.1))

    def test_ratio(self):
        '''Test that ratio correctly calculates the expected heights.'''
        self.assertTrue(self.original_ratio > self.item.original_ratio * 0.98)
        self.assertTrue(self.original_ratio < self.item.original_ratio * 1.02)
示例#8
0
    def __init__(self, theme):
        clutter.Group.__init__(self)
        self.animate_selector = False

        selector_filename = theme.getImage("selector")
        glow_filename = theme.getImage("selector_glow")

        # Set selector base texture
        self.selector = Texture(selector_filename)
        self.selector.set_opacity(200)
        self.add(self.selector)

        # Set selector GLOW texture
        self.glow = Texture(glow_filename)
        self.glow.set_opacity(0)
        self.add(self.glow)

        # Animate selector (Glow effect with glow overlay texture)
        self.in_time = clutter.Timeline(1500)
        self.in_alpha = clutter.Alpha(self.in_time, clutter.EASE_IN_OUT_SINE)
        self.in_behaviour = clutter.BehaviourOpacity(0, 255, self.in_alpha)
        self.in_behaviour.apply(self.glow)

        self.out_time = clutter.Timeline(1500)
        self.out_alpha = clutter.Alpha(self.out_time, clutter.EASE_IN_OUT_SINE)
        self.out_behaviour = clutter.BehaviourOpacity(255, 0, self.out_alpha)
        self.out_behaviour.apply(self.glow)

        self.score = clutter.Score()
        self.score.set_loop(True)
        self.score.append(timeline=self.in_time)
        # Link the out Timeline so that there is a smooth fade out.
        self.score.append(timeline=self.out_time, parent=self.in_time)
示例#9
0
    def _update_albumart(self, artist, title):
        """
        Search album art for current audio disc. This function is called only
        if album art doesn't exist already. If album art is found then we
        replace current disc icon with the new album art.
        @param artist: Artist name
        @param title: Album title
        """
        art_file = os.path.join(self.config.ALBUM_ART_DIR,
            artist + " - " + title + ".jpg")

        if os.path.exists(art_file):
            clutter.threads_enter()
            self.art2 = Texture(art_file, 0.1, 0.165)
            clutter.threads_leave()

            self.art2.set_size(self.get_abs_x(0.3148), self.get_abs_y(0.5599))
            self.art2.set_opacity(0)
            self.add(self.art2)

            timeline_in = clutter.Timeline(35, 26)
            alpha_in = clutter.Alpha(timeline_in, clutter.smoothstep_inc_func)
            self.in_behaviour = clutter.BehaviourOpacity(0, 255, alpha_in)
            self.in_behaviour.apply(self.art2)

            timeline_out = clutter.Timeline(35, 26)
            alpha_out = clutter.Alpha(timeline_out, clutter.smoothstep_inc_func)
            self.out_behaviour = clutter.BehaviourOpacity(255, 0, alpha_out)
            self.out_behaviour.apply(self.art)

            timeline_out.start()
            timeline_in.start()
示例#10
0
    def setUp(self):
        """Set up the test"""
        EntertainerTest.setUp(self)

        self.filename = os.path.join(THIS_DIR,
                                     'data/ImageThumbnailer/test.jpg')

        self.texture = Texture(self.filename, 0.1, 0.2)
示例#11
0
    def setUp(self):
        '''Set up the test.'''
        EntertainerTest.setUp(self)

        self.filename = os.path.join(THIS_DIR,
                                     'data/ImageThumbnailer/test.jpg')
        self.texture = Texture(self.filename)
        self.original_ratio = float(self.texture.get_width()) / \
            self.texture.get_height()

        self.item = ImageMenuItem(0.2, 0.1, self.texture)
示例#12
0
    def setUp(self):
        """Set up the test"""
        EntertainerTest.setUp(self)

        self.filename = os.path.join(THIS_DIR, 'data/ImageThumbnailer/test.jpg')

        self.texture = Texture(self.filename, 0.1, 0.2)
示例#13
0
    def setUp(self):
        '''Set up the test.'''
        EntertainerTest.setUp(self)

        self.menu = ImageMenu(0, 0)

        self.texture = Texture(os.path.join(
            THIS_DIR, 'data/ImageThumbnailer/test.jpg'))
示例#14
0
    def setUp(self):
        '''Set up the test.'''
        EntertainerTest.setUp(self)

        self.filename = os.path.join(THIS_DIR, 'data/ImageThumbnailer/test.jpg')
        self.texture = Texture(self.filename)
        self.original_ratio = float(self.texture.get_width()) / \
            self.texture.get_height()

        self.item = ImageMenuItem(0.2, 0.1, self.texture)
示例#15
0
    def create_day(self, day, x, y):
        """Create the Texture and labels for one day"""
        self.add(Texture(day["Image"], x, y))

        self.add(Label(0.04167, "text", x, y + 0.2, day["Day"],
            font_weight="bold"))

        conditions_text = \
            _("High: %(high)s   Low: %(low)s\nCondition: %(cond)s") % \
            {'high': day["High"], 'low': day["Low"], 'cond': day["Condition"]}
        self.add(Label(0.03, "text", x, y + 0.25, conditions_text))
示例#16
0
    def get_texture(self):
        '''Get media's texture. This is a video texture or album art texture.'''
        if self.media.get_type() == Playable.VIDEO_STREAM:
            return clutter.Clone(self.video_texture)

        elif self.media.get_type() == Playable.AUDIO_STREAM:
            url = self.media.get_album_art_url()
            if url is not None:
                texture = Texture(url)
                return texture
            else:
                return None
示例#17
0
    def _change_image(self, index):
        """
        Change current image. Display image from given index.
        """
        if self.texture:
            self.texture.destroy()

        # Create a new texture and display it
        image = self.images[index]
        self.index = index
        self.texture = Texture(image.get_filename())
        self._scale_image(self.texture)

        timeline = clutter.Timeline(1000)
        alpha = clutter.Alpha(timeline, clutter.EASE_IN_OUT_SINE)
        self.opacity_behaviour = clutter.BehaviourOpacity(alpha=alpha,
                                                          opacity_start=0,
                                                          opacity_end=255)
        self.opacity_behaviour.apply(self.texture)
        self.texture.set_opacity(0)
        timeline.start()

        self.add(self.texture)
示例#18
0
    def __init__(self, x=0, y=0, item_width=0.2, item_height=0.1):
        GridMenu.__init__(self, x, y, item_width, item_height)

        self.cursor_below = False
        self.horizontal = True
        self.items_per_col = 4
        self.visible_rows = 3
        self.visible_cols = 5

        c = clutter.Rectangle()
        c.set_size(100, 100)
        c.set_color((255, 255, 255, 128))
        self.cursor = c

        pix_buffer = gtk.gdk.pixbuf_new_from_file(
            self.config.theme.getImage("default_movie_art"))
        self.movie_default = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer)
        self.movie_default.hide()
        self.add(self.movie_default)

        self.album_default = Texture(
            self.config.theme.getImage("default_album_art"))
        self.album_default.hide()
        self.add(self.album_default)
示例#19
0
    def __init__(self, move_to_new_screen_callback, title, images):
        Screen.__init__(self, 'Photographs', move_to_new_screen_callback)

        self.images = images

        # Screen Title (Displayed at the bottom left corner)
        screen_title = Label(0.13, "screentitle", 0, 0.87, title)
        self.add(screen_title)

        # Image Title (over album list)
        self.image_title = Label(0.04167, "title", 0.0586, 0.7943, " ")
        self.image_title.set_ellipsize(pango.ELLIPSIZE_END)
        self.add(self.image_title)

        self.image_desc = Label(0.04167, "subtitle", 0.0586, 0.9115, " ")
        self.image_desc.set_line_wrap(True)
        self.image_desc.set_ellipsize(pango.ELLIPSIZE_END)
        self.add(self.image_desc)

        # Display throbber animation while loading photographs
        self.throbber = LoadingAnimation(0.9, 0.9)
        self.throbber.show()
        self.add(self.throbber)

        # List indicator
        self.li = None
        self._create_list_indicator()

        # Create photomenu
        self.menu = ImageMenu(0.03, 0.08, 0.12, self.y_for_x(0.12))
        self.menu.items_per_col = 3
        self.menu.visible_rows = 3
        self.menu.visible_cols = 8
        self.menu.active = True
        self.add(self.menu)

        photos = self.images
        photos_list = [[Texture(photo.get_thumbnail_url()), photo] \
            for photo in photos]
        self.menu.async_add(photos_list)

        self.menu.connect("selected", self._handle_select)
        self.menu.connect('moved', self._update_image_info)
        self.menu.connect("filled", self._on_menu_filled)
示例#20
0
    def show_empty_tab_notice(self, title=_("Empty tab"),
            message_body=_("This tab doesn't contain any elements.")):
        '''
        Create an information box that is displayed if there is no widgets in
        this tab. This method should be called only from child class as needed.
        '''

        # Create warning icon
        info_icon = Texture(self.theme.getImage("warning_icon"), 0.28, 0.27)
        self.add(info_icon)

        # Create warning title
        info_title = Label(0.0625, "title", 0.33, 0.27, title)
        self.add(info_title)

        # Create warning help text
        info = Label(0.042, "menuitem_inactive", 0.28, 0.4, message_body)
        info.width = 0.57
        self.add(info)
示例#21
0
    def async_add_albums(self, items):
        """
        Add asynchronously ImageMenuItem using a list.
        The created ImageMenuItem fits albums requirements.
        See also async_add comments.
        """
        if len(items) > 0:
            item = items[0]
            if item[1].has_album_art():
                texture = Texture(item[0])
            else:
                texture = clutter.Clone(self.album_default)

            self.add_item(texture, item[1])

            # Recursive call, remove first element from the list
            gobject.timeout_add(10, self.async_add_albums, items[1:])
        else:
            self.emit("filled")

        return False
示例#22
0
    def _change_image(self, index):
        """
        Change current image. Display image from given index.
        """
        if self.texture:
            self.texture.destroy()

        # Create a new texture and display it
        image = self.images[index]
        self.index = index
        self.texture = Texture(image.get_filename())
        self._scale_image(self.texture)

        timeline = clutter.Timeline(1000)
        alpha = clutter.Alpha(timeline, clutter.EASE_IN_OUT_SINE)
        self.opacity_behaviour = clutter.BehaviourOpacity(alpha=alpha,
            opacity_start=0, opacity_end=255)
        self.opacity_behaviour.apply(self.texture)
        self.texture.set_opacity(0)
        timeline.start()

        self.add(self.texture)
示例#23
0
    def _create_no_photos_information(self):
        """
        Create textures and labels for information screen. This is displayed
        instead of album list if there are no photos available and it helps
        users to add new photographs to the system.
        """
        # Create warning icon
        warning_icon = Texture(self.theme.getImage("warning_icon"), 0.28, 0.27)
        self.add(warning_icon)

        # Create warning title
        info_title = Label(0.0625, "title", 0.3367, 0.2709,
                           _("No photographs available!"))
        self.add(info_title)

        # Create warning help text
        message = _(
            "There are no indexed photographs in the Entertainer media "
            "library. To add photographs, start the Content management tool "
            "and open the 'Images' tab. Now click on the 'Add' button and "
            "select some folders which contain image files.")
        info = Label(0.0417, "menuitem_inactive", 0.2804, 0.45, message)
        info.set_size(0.5, 0.5859)
        self.add(info)
示例#24
0
    def _create_no_music_information(self):
        """
        Create textures and labels for information screen. This is displayed
        instead of artist list if there are no tracks available and it helps
        users to add new music to the system.
        """
        # Create warning icon
        warning_icon = Texture(self.theme.getImage("warning_icon"), 0.28, 0.27)
        self.add(warning_icon)

        # Create warning title
        info_title = Label(0.0625, "title", 0.3367, 0.2709,
                           _("No music available!"))
        self.add(info_title)

        # Create warning help text
        message = _(
            "There are no indexed artists in the Entertainer media "
            "library. To add music, start the Content management tool "
            "and open the 'Music' tab. Now click on the 'Add' button and "
            "select some folders which contain music files.")
        info = Label(0.0417, "menuitem_inactive", 0.2804, 0.45, message)
        info.width = 0.5
        self.add(info)
示例#25
0
    def __init__(self, x=0, y=0, item_width=0.2, item_height=0.1):
        GridMenu.__init__(self, x, y, item_width, item_height)

        self.cursor_below = False
        self.horizontal = True
        self.items_per_col = 4
        self.visible_rows = 3
        self.visible_cols = 5

        c = clutter.Rectangle()
        c.set_size(100, 100)
        c.set_color((255, 255, 255, 128))
        self.cursor = c

        pix_buffer = gtk.gdk.pixbuf_new_from_file(
            self.config.theme.getImage("default_movie_art"))
        self.movie_default = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer)
        self.movie_default.hide()
        self.add(self.movie_default)

        self.album_default = Texture(
            self.config.theme.getImage("default_album_art"))
        self.album_default.hide()
        self.add(self.album_default)
示例#26
0
class VideoOSD(Screen):
    '''Screen is displayed when video is being watched.

    Usually this screen doesn't have any visible elements. User actions such
    as pause and rewind are displayed as on-screen-graphics.'''

    def __init__(self, media_player):
        Screen.__init__(self, 'VideoOSD', kind=Screen.OSD)

        self.theme = self.config.theme
        self.media_player = media_player
        self.media_player.connect('play', self._handle_play_pause)
        self.media_player.connect('pause', self._handle_play_pause)
        self.media_player.connect('skip-forward', self._handle_skip_forward)
        self.media_player.connect('skip-backward', self._handle_skip_backward)

        self._progress_bar = self._create_progress_bar()
        self.add(self._progress_bar)
        self.aspect_textures = None
        self.pause_texture = None
        self.seekbackward_texture = None
        self.seekforward_texture = None

        self.timeout_key = None
        self.progress_bar_timeout_key = None

        self.event_handlers.update({
            UserEvent.PLAYER_PLAY_PAUSE : self._handle_play_pause,
            UserEvent.PLAYER_SKIP_BACKWARD : self._handle_skip_backward,
            UserEvent.PLAYER_SKIP_FORWARD : self._handle_skip_forward,
            UserEvent.PLAYER_STOP : self._handle_stop,
            UserEvent.USE_ASPECT_RATIO_1 : self._handle_ratio_1,
            UserEvent.USE_ASPECT_RATIO_2 : self._handle_ratio_2,
            UserEvent.USE_ASPECT_RATIO_3 : self._handle_ratio_3,
            UserEvent.USE_ASPECT_RATIO_4 : self._handle_ratio_4
        })

        self._create_aspect_ratio_textures()

        self._create_navigation_textures()

    def _create_navigation_textures(self):
        '''Create the pause, seek-backward & seek-forward textures.'''
        self.pause_texture = Texture(
            self.theme.getImage("media-playback-pause"), 0.5, 0.5)
        self.pause_texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
        self.pause_texture.hide()
        self.add(self.pause_texture)

        pause_in_time = clutter.Timeline(1000)
        in_alpha_pause = clutter.Alpha(pause_in_time, clutter.EASE_IN_OUT_SINE)

        self.pause_in_opacity = clutter.BehaviourOpacity(alpha=in_alpha_pause,
            opacity_start=100, opacity_end=255)
        self.pause_in_scale = clutter.BehaviourScale(1.0, 1.0, 1.4, 1.4,
            in_alpha_pause)

        self.pause_in_opacity.apply(self.pause_texture)
        self.pause_in_scale.apply(self.pause_texture)

        pause_out_time = clutter.Timeline(1000)
        out_alpha_pause = clutter.Alpha(pause_out_time,
            clutter.EASE_IN_OUT_SINE)

        self.pause_out_opacity = clutter.BehaviourOpacity(alpha=out_alpha_pause,
            opacity_start=255, opacity_end=100)
        self.pause_out_scale = clutter.BehaviourScale(1.4, 1.4, 1.0, 1.0,
            out_alpha_pause)

        self.pause_out_opacity.apply(self.pause_texture)
        self.pause_out_scale.apply(self.pause_texture)

        self.score = clutter.Score()
        self.score.set_loop(True)
        self.score.append(timeline=pause_in_time)
        self.score.append(timeline=pause_out_time, parent=pause_in_time)
        self.score.start()

        self.seekbackward_texture = Texture(
            self.theme.getImage("media-seek-backward"), 0.1, 0.5)
        self.seekbackward_texture.set_anchor_point_from_gravity(
            clutter.GRAVITY_CENTER)
        self.seekbackward_texture.set_opacity(0)
        self.add(self.seekbackward_texture)

        self.seekbackward_timeline = clutter.Timeline(1000)
        alpha_seekbackward = clutter.Alpha(self.seekbackward_timeline,
            clutter.EASE_IN_OUT_SINE)

        self.seekbackward_opacity = clutter.BehaviourOpacity(
            alpha=alpha_seekbackward, opacity_start=255, opacity_end=0)
        self.seekbackward_opacity.apply(self.seekbackward_texture)

        self.seekforward_texture = Texture(
            self.theme.getImage("media-seek-forward"), 0.9, 0.5)
        self.seekforward_texture.set_anchor_point_from_gravity(
            clutter.GRAVITY_CENTER)
        self.seekforward_texture.set_opacity(0)
        self.add(self.seekforward_texture)

        self.seekforward_timeline = clutter.Timeline(1000)
        alpha_seekforward = clutter.Alpha(self.seekforward_timeline,
            clutter.EASE_IN_OUT_SINE)

        self.seekforward_opacity = clutter.BehaviourOpacity(
            alpha=alpha_seekforward, opacity_start=255, opacity_end=0)
        self.seekforward_opacity.apply(self.seekforward_texture)

    def _create_progress_bar(self):
        '''Create the progress bar.'''
        progress_bar = ProgressBar(0.5, 0.9, 0.40, 0.04)
        progress_bar.auto_display = True
        progress_bar.media_player = self.media_player
        progress_bar.visible = True
        return progress_bar

    def _create_aspect_ratio_textures(self):
        '''
        Create textures that which are displayed when user changes aspect ratio.
        '''
        texture_1 = Texture(self.theme.getImage("native_aspect_ratio"))
        texture_1.hide()
        self.add(texture_1)
        texture_2 = Texture(self.theme.getImage("widescreen_aspect_ratio"))
        texture_2.hide()
        self.add(texture_2)
        texture_3 = Texture(self.theme.getImage("zoom_aspect_ratio"))
        texture_3.hide()
        self.add(texture_3)
        texture_4 = Texture(self.theme.getImage("compromise_aspect_ratio"))
        texture_4.hide()
        self.add(texture_4)
        self.aspect_textures = [texture_1, texture_2, texture_3, texture_4]
        self.timeout_key = None # This is used when canceling timeouts
        for texture in self.aspect_textures:
            texture.position = (
                float(self.config.stage_width - texture.get_width()) /
                (self.config.stage_width * 2), 0.67)

    def _hide_aspect_ratio_logo(self, number):
        '''
        Hide aspect ratio texture. This is a callback function and
        shouldn't be called directly.
        '''
        self.aspect_textures[number].hide()
        self.timeout_key = None
        return False

    def _display_aspect_ratio_logo(self, number):
        '''Display aspect ratio logo on screen when ratio is changed.'''
        if self.timeout_key is not None:
            gobject.source_remove(self.timeout_key)
        for texture in self.aspect_textures:
            texture.hide()
        self.aspect_textures[number].show()
        self.timeout_key = gobject.timeout_add(2000,
            self._hide_aspect_ratio_logo, number)

    def _handle_play_pause(self, event=None):
        '''Handle UserEvent.PLAYER_PLAY_PAUSE.'''
        if self.media_player.is_playing:
            self.pause_texture.hide()
        else:
            self.pause_texture.show()
        self._progress_bar.visible = True

    def _handle_skip_backward(self, event=None):
        '''Handle UserEvent.PLAYER_SKIP_BACKWARD.'''
        self._progress_bar.visible = True
        self.seekbackward_timeline.start()

    def _handle_skip_forward(self, event=None):
        '''Handle UserEvent.PLAYER_SKIP_FORWARD.'''
        self._progress_bar.visible = True
        self.seekforward_timeline.start()

    def _handle_stop(self):
        '''Handle UserEvent.PLAYER_STOP.'''
        self._progress_bar.visible = True
        self.pause_texture.hide()

    def _handle_ratio_1(self):
        '''Handle UserEvent.USE_ASPECT_RATIO_1.'''
        self._display_aspect_ratio_logo(0)

    def _handle_ratio_2(self):
        '''Handle UserEvent.USE_ASPECT_RATIO_2.'''
        self._display_aspect_ratio_logo(1)

    def _handle_ratio_3(self):
        '''Handle UserEvent.USE_ASPECT_RATIO_3.'''
        self._display_aspect_ratio_logo(2)

    def _handle_ratio_4(self):
        '''Handle UserEvent.USE_ASPECT_RATIO_4.'''
        self._display_aspect_ratio_logo(3)
示例#27
0
class Photo(Screen):
    '''Screen displays photograph in fullscreen and allows user to zoom in.'''

    # How much one zoom click zooms in
    ZOOM_FACTOR = 0.5
    MOVE_SIZE = 100

    def __init__(self, current_photo_index, images):
        Screen.__init__(self, 'Photo')

        self.animate = self.config.show_effects
        self.slideshow_step = self.config.slideshow_step

        self.zoom_level = 1
        self.index = current_photo_index
        self.images = images
        self.slideshow = False
        # Slideshow gobject timeout tag
        self.slideshow_timeout_tag = None

        # Create black background
        self.background = clutter.Rectangle()
        self.background.set_size(
            self.config.stage_width, self.config.stage_height)
        self.background.set_color((0, 0, 0, 255))
        self.add(self.background)

        # Screen Title (Displayed at the bottom left corner)
        self.screen_title = Label(0.13, "screentitle", 0, 0.87,
            _("Information"))
        self.add(self.screen_title)

        self.texture = None
        self._change_image(self.index)

    def set_animate(self, boolean):
        """Animate this screen."""
        self.animate = boolean

    def _change_image(self, index):
        """
        Change current image. Display image from given index.
        """
        if self.texture:
            self.texture.destroy()

        # Create a new texture and display it
        image = self.images[index]
        self.index = index
        self.texture = Texture(image.get_filename())
        self._scale_image(self.texture)

        timeline = clutter.Timeline(1000)
        alpha = clutter.Alpha(timeline, clutter.EASE_IN_OUT_SINE)
        self.opacity_behaviour = clutter.BehaviourOpacity(alpha=alpha,
            opacity_start=0, opacity_end=255)
        self.opacity_behaviour.apply(self.texture)
        self.texture.set_opacity(0)
        timeline.start()

        self.add(self.texture)

    def _scale_image(self, texture, zoom_level=1):
        """
        Scale image. Scaling doesn't change image aspect ratio.
        @param texture: Texture to scale
        @param zoom_level: Zoom level - Default value is 1 (no zoom)
        """
        # Center position when zoomed
        width = texture.get_width()
        height = texture.get_height()
        x_ratio = self.config.stage_width / float(width)
        y_ratio = self.config.stage_height / float(height)

        if x_ratio > y_ratio:
            texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
            texture.set_scale(
                self.config.stage_height / float(height) * zoom_level,
                self.config.stage_height / float(height) * zoom_level)
            texture.set_anchor_point(0, 0)

            if zoom_level == 1: # Center image if in normal size
                new_width = int(width *
                    (self.config.stage_height / float(height)))
                new_x = int(
                    (self.config.stage_width - new_width) / float(2))
                texture.set_position(new_x, 0)
        else:
            texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
            texture.set_scale(
                self.config.stage_width / float(width) * zoom_level,
                self.config.stage_width / float(width) * zoom_level)
            texture.set_anchor_point(0, 0)

            if zoom_level == 1:  # Center image if in normal size
                new_height = int(
                    height * (self.config.stage_width / float(width)))
                new_y = int((self.config.stage_height - new_height) / float(2))
                texture.set_position(0, new_y)

    def is_interested_in_play_action(self):
        """
        Override function from Screen class. See Screen class for
        better documentation.
        """
        return True

    def execute_play_action(self):
        """
        Override function from Screen class. See Screen class for
        better documentation.
        """
        if self.slideshow:
            self.stop_slideshow()
        else:
            self.start_slideshow()

    def slideshow_progress(self):
        """slideshow loop called every slideshow_step seconds"""
        if ((self.zoom_level == 1) and self.slideshow):
            self._handle_right()
        return True

    def start_slideshow(self):
        """Start the slideshow"""
        self.zoom_level = 1
        self.slideshow = True
        self.slideshow_progress()
        self.slideshow_timeout_tag = gobject.timeout_add(self.slideshow_step *
            1000, self.slideshow_progress)

    def stop_slideshow(self):
        """Stop the slideshow"""
        self.slideshow = False
        if self.slideshow_timeout_tag:
            gobject.source_remove(self.slideshow_timeout_tag)

    def _handle_up(self):
        '''Handle UserEvent.NAVIGATE_UP.'''
        if self.zoom_level != 1:
            self.texture.move_by(0, self.MOVE_SIZE)

    def _handle_down(self):
        '''Handle UserEvent.NAVIGATE_DOWN.'''
        if self.zoom_level != 1:
            self.texture.move_by(0, -self.MOVE_SIZE)

    def _handle_left(self):
        '''Handle UserEvent.NAVIGATE_LEFT.'''
        # Change to previous image
        if self.zoom_level == 1:
            if self.index == 0:
                self.index = len(self.images) - 1
            else:
                self.index = self.index - 1
            self._change_image(self.index)
        # Change texture position (texture is zoomed)
        else:
            self.texture.move_by(self.MOVE_SIZE, 0)

    def _handle_right(self):
        '''Handle UserEvent.NAVIGATE_RIGHT.'''
        # Change to next image
        if self.zoom_level == 1:
            if self.index == len(self.images) - 1:
                self.index = 0
            else:
                self.index = self.index + 1
            self._change_image(self.index)
        # Change texture position (texture is zoomed)
        else:
            self.texture.move_by(-self.MOVE_SIZE, 0)

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        # Zoom image. If we zoom then we stop sliding photos.
        self.stop_slideshow()
        if self.zoom_level >= 3:
            self.zoom_level = 1
        else:
            self.zoom_level = self.zoom_level + self.ZOOM_FACTOR
        self._scale_image(self.texture, self.zoom_level)
示例#28
0
class ImageMenu(GridMenu):
    """A grid menu that contains images."""
    __gsignals__ = {
        'filled' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
    }

    def __init__(self, x=0, y=0, item_width=0.2, item_height=0.1):
        GridMenu.__init__(self, x, y, item_width, item_height)

        self.cursor_below = False
        self.horizontal = True
        self.items_per_col = 4
        self.visible_rows = 3
        self.visible_cols = 5

        c = clutter.Rectangle()
        c.set_size(100, 100)
        c.set_color((255, 255, 255, 128))
        self.cursor = c

        pix_buffer = gtk.gdk.pixbuf_new_from_file(
            self.config.theme.getImage("default_movie_art"))
        self.movie_default = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer)
        self.movie_default.hide()
        self.add(self.movie_default)

        self.album_default = Texture(
            self.config.theme.getImage("default_album_art"))
        self.album_default.hide()
        self.add(self.album_default)

    def add_item(self, texture, data):
        """Add a ImageMenuItem from a Texture."""
        item = ImageMenuItem(self._item_width, self._item_height, texture)
        item.userdata = data

        self.raw_add_item(item)

    def async_add(self, items):
        """
        Add asynchronously ImageMenuItem using a list.

        The list should be : [[texture1, data1], [texture2, data2], etc]

        texture1, texture2 : are Texture objects.
        data1, data2: are the data that will be accessible from the menu item.
        (see MenuItem Class)
        """
        if len(items) > 0:
            item = items[0]
            self.add_item(item[0], item[1])

            # Recursive call, remove first element from the list
            gobject.timeout_add(15, self.async_add, items[1:])
        else:
            self.emit("filled")

        return False

    # XXX: This needs to be changed. An ImageMenu should know nothing about
    # special video items.
    def async_add_videos(self, items):
        """
        Add asynchronously ImageMenuItem using a list.
        The created ImageMenuItem fits movies and series requirements.
        See also async_add comments.
        """
        if len(items) > 0:
            item = items[0]
            if item[1].has_cover_art():
                pix_buffer = gtk.gdk.pixbuf_new_from_file(item[0])
                texture = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer)
            else:
                texture = clutter.Clone(self.movie_default)

            self.add_item(texture, item[1])

            # Recursive call, remove first element from the list
            gobject.timeout_add(10, self.async_add_videos, items[1:])
        else:
            self.emit("filled")

        return False

    # XXX: This needs to be changed. An ImageMenu should know nothing about
    # special album items.
    def async_add_albums(self, items):
        """
        Add asynchronously ImageMenuItem using a list.
        The created ImageMenuItem fits albums requirements.
        See also async_add comments.
        """
        if len(items) > 0:
            item = items[0]
            if item[1].has_album_art():
                texture = Texture(item[0])
            else:
                texture = clutter.Clone(self.album_default)

            self.add_item(texture, item[1])

            # Recursive call, remove first element from the list
            gobject.timeout_add(10, self.async_add_albums, items[1:])
        else:
            self.emit("filled")

        return False

    # XXX: This needs to be changed. An ImageMenu should know nothing about
    # special clip items.
    def async_add_clips(self, items):
        """
        Add asynchronously ImageMenuItem using a list.
        The created ImageMenuItem fits clips requirements.
        See also async_add comments.
        """
        if len(items) > 0:
            item = items[0]
            pix_buffer = gtk.gdk.pixbuf_new_from_file(item[0])
            texture = RoundedTexture(0.0, 0.0, 0.23, self.y_for_x(0.23) * 0.7,
                pix_buffer)

            self.add_item(texture, item[1])

            # Recursive call, remove first element from the list
            gobject.timeout_add(10, self.async_add_clips, items[1:])
        else:
            self.emit("filled")

        return False
示例#29
0
class Photo(Screen):
    '''Screen displays photograph in fullscreen and allows user to zoom in.'''

    # How much one zoom click zooms in
    ZOOM_FACTOR = 0.5
    MOVE_SIZE = 100

    def __init__(self, current_photo_index, images):
        Screen.__init__(self, 'Photo')

        self.animate = self.config.show_effects
        self.slideshow_step = self.config.slideshow_step

        self.zoom_level = 1
        self.index = current_photo_index
        self.images = images
        self.slideshow = False
        # Slideshow gobject timeout tag
        self.slideshow_timeout_tag = None

        # Create black background
        self.background = clutter.Rectangle()
        self.background.set_size(self.config.stage_width,
                                 self.config.stage_height)
        self.background.set_color((0, 0, 0, 255))
        self.add(self.background)

        # Screen Title (Displayed at the bottom left corner)
        self.screen_title = Label(0.13, "screentitle", 0, 0.87,
                                  _("Information"))
        self.add(self.screen_title)

        self.texture = None
        self._change_image(self.index)

    def set_animate(self, boolean):
        """Animate this screen."""
        self.animate = boolean

    def _change_image(self, index):
        """
        Change current image. Display image from given index.
        """
        if self.texture:
            self.texture.destroy()

        # Create a new texture and display it
        image = self.images[index]
        self.index = index
        self.texture = Texture(image.get_filename())
        self._scale_image(self.texture)

        timeline = clutter.Timeline(1000)
        alpha = clutter.Alpha(timeline, clutter.EASE_IN_OUT_SINE)
        self.opacity_behaviour = clutter.BehaviourOpacity(alpha=alpha,
                                                          opacity_start=0,
                                                          opacity_end=255)
        self.opacity_behaviour.apply(self.texture)
        self.texture.set_opacity(0)
        timeline.start()

        self.add(self.texture)

    def _scale_image(self, texture, zoom_level=1):
        """
        Scale image. Scaling doesn't change image aspect ratio.
        @param texture: Texture to scale
        @param zoom_level: Zoom level - Default value is 1 (no zoom)
        """
        # Center position when zoomed
        width = texture.get_width()
        height = texture.get_height()
        x_ratio = self.config.stage_width / float(width)
        y_ratio = self.config.stage_height / float(height)

        if x_ratio > y_ratio:
            texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
            texture.set_scale(
                self.config.stage_height / float(height) * zoom_level,
                self.config.stage_height / float(height) * zoom_level)
            texture.set_anchor_point(0, 0)

            if zoom_level == 1:  # Center image if in normal size
                new_width = int(width *
                                (self.config.stage_height / float(height)))
                new_x = int((self.config.stage_width - new_width) / float(2))
                texture.set_position(new_x, 0)
        else:
            texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
            texture.set_scale(
                self.config.stage_width / float(width) * zoom_level,
                self.config.stage_width / float(width) * zoom_level)
            texture.set_anchor_point(0, 0)

            if zoom_level == 1:  # Center image if in normal size
                new_height = int(height *
                                 (self.config.stage_width / float(width)))
                new_y = int((self.config.stage_height - new_height) / float(2))
                texture.set_position(0, new_y)

    def is_interested_in_play_action(self):
        """
        Override function from Screen class. See Screen class for
        better documentation.
        """
        return True

    def execute_play_action(self):
        """
        Override function from Screen class. See Screen class for
        better documentation.
        """
        if self.slideshow:
            self.stop_slideshow()
        else:
            self.start_slideshow()

    def slideshow_progress(self):
        """slideshow loop called every slideshow_step seconds"""
        if ((self.zoom_level == 1) and self.slideshow):
            self._handle_right()
        return True

    def start_slideshow(self):
        """Start the slideshow"""
        self.zoom_level = 1
        self.slideshow = True
        self.slideshow_progress()
        self.slideshow_timeout_tag = gobject.timeout_add(
            self.slideshow_step * 1000, self.slideshow_progress)

    def stop_slideshow(self):
        """Stop the slideshow"""
        self.slideshow = False
        if self.slideshow_timeout_tag:
            gobject.source_remove(self.slideshow_timeout_tag)

    def _handle_up(self):
        '''Handle UserEvent.NAVIGATE_UP.'''
        if self.zoom_level != 1:
            self.texture.move_by(0, self.MOVE_SIZE)

    def _handle_down(self):
        '''Handle UserEvent.NAVIGATE_DOWN.'''
        if self.zoom_level != 1:
            self.texture.move_by(0, -self.MOVE_SIZE)

    def _handle_left(self):
        '''Handle UserEvent.NAVIGATE_LEFT.'''
        # Change to previous image
        if self.zoom_level == 1:
            if self.index == 0:
                self.index = len(self.images) - 1
            else:
                self.index = self.index - 1
            self._change_image(self.index)
        # Change texture position (texture is zoomed)
        else:
            self.texture.move_by(self.MOVE_SIZE, 0)

    def _handle_right(self):
        '''Handle UserEvent.NAVIGATE_RIGHT.'''
        # Change to next image
        if self.zoom_level == 1:
            if self.index == len(self.images) - 1:
                self.index = 0
            else:
                self.index = self.index + 1
            self._change_image(self.index)
        # Change texture position (texture is zoomed)
        else:
            self.texture.move_by(-self.MOVE_SIZE, 0)

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        # Zoom image. If we zoom then we stop sliding photos.
        self.stop_slideshow()
        if self.zoom_level >= 3:
            self.zoom_level = 1
        else:
            self.zoom_level = self.zoom_level + self.ZOOM_FACTOR
        self._scale_image(self.texture, self.zoom_level)
示例#30
0
class Selector(clutter.Group):
    '''Selector is an animated texture that can be used to display current
    position in menus.'''

    def __init__(self, theme):
        clutter.Group.__init__(self)
        self.animate_selector = False

        selector_filename = theme.getImage("selector")
        glow_filename = theme.getImage("selector_glow")

        # Set selector base texture
        self.selector = Texture(selector_filename)
        self.selector.set_opacity(200)
        self.add(self.selector)

        # Set selector GLOW texture
        self.glow = Texture(glow_filename)
        self.glow.set_opacity(0)
        self.add(self.glow)

        # Animate selector (Glow effect with glow overlay texture)
        self.in_time = clutter.Timeline(1500)
        self.in_alpha = clutter.Alpha(self.in_time, clutter.EASE_IN_OUT_SINE)
        self.in_behaviour = clutter.BehaviourOpacity(0, 255, self.in_alpha)
        self.in_behaviour.apply(self.glow)

        self.out_time = clutter.Timeline(1500)
        self.out_alpha = clutter.Alpha(self.out_time, clutter.EASE_IN_OUT_SINE)
        self.out_behaviour = clutter.BehaviourOpacity(255, 0, self.out_alpha)
        self.out_behaviour.apply(self.glow)

        self.score = clutter.Score()
        self.score.set_loop(True)
        self.score.append(timeline=self.in_time)
        # Link the out Timeline so that there is a smooth fade out.
        self.score.append(timeline=self.out_time, parent=self.in_time)

    def set_size(self, width, height):
        """Overrides clutter.Actor.set_size()"""
        self.selector.set_size(width, height)
        self.glow.set_size(width, height)

    def stop_animation(self):
        """Stop selector animation"""
        self.score.stop()
        self.glow.set_opacity(0)

    def start_animation(self):
        """Start selector animation"""
        self.score.start()

    def hide(self):
        """Overrides clutter.Actor.hide()"""
        clutter.Group.hide(self)
        if self.animate_selector:
            self.stop_animation()

    def show(self):
        """Overrides clutter.Actor.show()"""
        clutter.Group.show(self)
        self.start_animation()

    def hide_all(self):
        """Overrides clutter.Actor.hide_all()"""
        clutter.Group.hide_all(self)
        self.stop_animation()

    def show_all(self):
        """Overrides clutter.Actor.show_all()"""
        clutter.Group.show_all(self)
        self.start_animation()
示例#31
0
    def _create_navigation_textures(self):
        '''Create the pause, seek-backward & seek-forward textures.'''
        self.pause_texture = Texture(
            self.theme.getImage("media-playback-pause"), 0.5, 0.5)
        self.pause_texture.set_anchor_point_from_gravity(
            clutter.GRAVITY_CENTER)
        self.pause_texture.hide()
        self.add(self.pause_texture)

        pause_in_time = clutter.Timeline(1000)
        in_alpha_pause = clutter.Alpha(pause_in_time, clutter.EASE_IN_OUT_SINE)

        self.pause_in_opacity = clutter.BehaviourOpacity(alpha=in_alpha_pause,
                                                         opacity_start=100,
                                                         opacity_end=255)
        self.pause_in_scale = clutter.BehaviourScale(1.0, 1.0, 1.4, 1.4,
                                                     in_alpha_pause)

        self.pause_in_opacity.apply(self.pause_texture)
        self.pause_in_scale.apply(self.pause_texture)

        pause_out_time = clutter.Timeline(1000)
        out_alpha_pause = clutter.Alpha(pause_out_time,
                                        clutter.EASE_IN_OUT_SINE)

        self.pause_out_opacity = clutter.BehaviourOpacity(
            alpha=out_alpha_pause, opacity_start=255, opacity_end=100)
        self.pause_out_scale = clutter.BehaviourScale(1.4, 1.4, 1.0, 1.0,
                                                      out_alpha_pause)

        self.pause_out_opacity.apply(self.pause_texture)
        self.pause_out_scale.apply(self.pause_texture)

        self.score = clutter.Score()
        self.score.set_loop(True)
        self.score.append(timeline=pause_in_time)
        self.score.append(timeline=pause_out_time, parent=pause_in_time)
        self.score.start()

        self.seekbackward_texture = Texture(
            self.theme.getImage("media-seek-backward"), 0.1, 0.5)
        self.seekbackward_texture.set_anchor_point_from_gravity(
            clutter.GRAVITY_CENTER)
        self.seekbackward_texture.set_opacity(0)
        self.add(self.seekbackward_texture)

        self.seekbackward_timeline = clutter.Timeline(1000)
        alpha_seekbackward = clutter.Alpha(self.seekbackward_timeline,
                                           clutter.EASE_IN_OUT_SINE)

        self.seekbackward_opacity = clutter.BehaviourOpacity(
            alpha=alpha_seekbackward, opacity_start=255, opacity_end=0)
        self.seekbackward_opacity.apply(self.seekbackward_texture)

        self.seekforward_texture = Texture(
            self.theme.getImage("media-seek-forward"), 0.9, 0.5)
        self.seekforward_texture.set_anchor_point_from_gravity(
            clutter.GRAVITY_CENTER)
        self.seekforward_texture.set_opacity(0)
        self.add(self.seekforward_texture)

        self.seekforward_timeline = clutter.Timeline(1000)
        alpha_seekforward = clutter.Alpha(self.seekforward_timeline,
                                          clutter.EASE_IN_OUT_SINE)

        self.seekforward_opacity = clutter.BehaviourOpacity(
            alpha=alpha_seekforward, opacity_start=255, opacity_end=0)
        self.seekforward_opacity.apply(self.seekforward_texture)
示例#32
0
    def create_movie_information(self):
        '''Create clutter parts related to movie information'''

        # Movie art texture
        if self.movie.has_cover_art():
            pixbuf = gtk.gdk.pixbuf_new_from_file(self.movie.cover_art_url)
        else:
            pixbuf = gtk.gdk.pixbuf_new_from_file(
                self.theme.getImage("default_movie_art"))
        movie_art = EyeCandyTexture(0.33, 0.1, 0.1, 0.25, pixbuf)
        self.add(movie_art)

        # Movie title
        title = Label(0.04, "title", 0.47, 0.1, self.movie.title,
            font_weight="bold")
        title.set_ellipsize(pango.ELLIPSIZE_END)
        title.set_size(0.5124, 0.05208)
        self.add(title)

        # Movie release year
        year_text = _("Released in %(year)s") % {'year': self.movie.year}
        year = Label(0.032, "subtitle", 0.47, 0.3, year_text)
        year.set_ellipsize(pango.ELLIPSIZE_END)
        year.set_size(0.5124, 0.05208)
        self.add(year)

        # Show only 2 genres (or one if there is only one)
        genres_list = self.movie.genres
        if len(genres_list) == 0:
            genres_text = _("Unknown")
        else:
            genres_text = "/".join(genres_list[:2])

        # Runtime and genres
        info_text = _("%(runtime)s min, %(genre)s") % \
            {'runtime': self.movie.runtime, 'genre': genres_text}
        info = Label(0.032, "subtitle", 0.47, 0.24, info_text)
        info.set_ellipsize(pango.ELLIPSIZE_END)
        info.set_size(0.5124, 0.05208)
        self.add(info)

        # Stars (rating)
        star = Texture(self.theme.getImage("star"))
        star.hide()
        self.add(star)
        star2 = Texture(self.theme.getImage("star2"))
        star2.hide()
        self.add(star2)

        for i in range(self.movie.rating):
            tex = clutter.Clone(star)
            tex.set_position(
                self.get_abs_x(0.47) + (self.get_abs_x(0.0366) * i),
                self.get_abs_y(0.17))
            tex.set_size(self.get_abs_x(0.024), self.get_abs_y(0.04))
            self.add(tex)

        dark_star = 5 - self.movie.rating
        for i in range(dark_star):
            tex = clutter.Clone(star2)
            tex.set_position(self.get_abs_x(0.47) + (self.get_abs_x(0.0366) * \
                (i + self.movie.rating)), self.get_abs_y(0.17))
            tex.set_size(self.get_abs_x(0.024), self.get_abs_y(0.04))
            self.add(tex)

        # Plot
        plot = Label(0.029, "subtitle", 0, 0, self.movie.plot)
        plot.set_justify(True)
        plot.set_line_wrap_mode(pango.WRAP_WORD)
        plot.set_line_wrap(True)
        plot.width = 0.5124
        self.scroll_area = ScrollArea(0.33, 0.38, 0.5124, 0.3516, plot)
        self.add(self.scroll_area)

        # Actors
        self.add(Label(0.032, "title", 0.33, 0.8, _("Starring")))

        actors_list = self.movie.actors
        if len(actors_list) == 0:
            actors_text = _("Unknown")
        else:
            actors_text = ", ".join(actors_list[:5])
        actors = Label(0.032, "subtitle", 0.46, 0.8, actors_text)
        actors.set_ellipsize(pango.ELLIPSIZE_END)
        actors.set_size(0.5124, 0.05208)
        self.add(actors)

        # Directors
        self.add(Label(0.032, "title", 0.33, 0.86, _("Directed by")))

        directors_list = self.movie.directors
        if len(directors_list) == 0:
            directors_text = _("Unknown")
        else:
            directors_text = ", ".join(directors_list[:2])
        directors = Label(0.032, "subtitle", 0.46, 0.86, directors_text)
        directors.set_ellipsize(pango.ELLIPSIZE_END)
        directors.set_size(0.5124, 0.05208)
        self.add(directors)

        # Writers
        self.add(Label(0.032, "title", 0.33, 0.92, _("Written by")))

        writers_list = self.movie.writers
        if len(directors_list) == 0:
            writers_text = _("Unknown")
        else:
            writers_text = ", ".join(writers_list[:2])
        writers = Label(0.032, "subtitle", 0.46, 0.92, writers_text)
        writers.set_ellipsize(pango.ELLIPSIZE_END)
        writers.set_size(0.5124, 0.05208)
        self.add(writers)
示例#33
0
class VideoOSD(Screen):
    '''Screen is displayed when video is being watched.

    Usually this screen doesn't have any visible elements. User actions such
    as pause and rewind are displayed as on-screen-graphics.'''
    def __init__(self, media_player):
        Screen.__init__(self, 'VideoOSD', kind=Screen.OSD)

        self.theme = self.config.theme
        self.media_player = media_player
        self.media_player.connect('play', self._handle_play_pause)
        self.media_player.connect('pause', self._handle_play_pause)
        self.media_player.connect('skip-forward', self._handle_skip_forward)
        self.media_player.connect('skip-backward', self._handle_skip_backward)

        self._progress_bar = self._create_progress_bar()
        self.add(self._progress_bar)
        self.aspect_textures = None
        self.pause_texture = None
        self.seekbackward_texture = None
        self.seekforward_texture = None

        self.timeout_key = None
        self.progress_bar_timeout_key = None

        self.event_handlers.update({
            UserEvent.PLAYER_PLAY_PAUSE:
            self._handle_play_pause,
            UserEvent.PLAYER_SKIP_BACKWARD:
            self._handle_skip_backward,
            UserEvent.PLAYER_SKIP_FORWARD:
            self._handle_skip_forward,
            UserEvent.PLAYER_STOP:
            self._handle_stop,
            UserEvent.USE_ASPECT_RATIO_1:
            self._handle_ratio_1,
            UserEvent.USE_ASPECT_RATIO_2:
            self._handle_ratio_2,
            UserEvent.USE_ASPECT_RATIO_3:
            self._handle_ratio_3,
            UserEvent.USE_ASPECT_RATIO_4:
            self._handle_ratio_4
        })

        self._create_aspect_ratio_textures()

        self._create_navigation_textures()

    def _create_navigation_textures(self):
        '''Create the pause, seek-backward & seek-forward textures.'''
        self.pause_texture = Texture(
            self.theme.getImage("media-playback-pause"), 0.5, 0.5)
        self.pause_texture.set_anchor_point_from_gravity(
            clutter.GRAVITY_CENTER)
        self.pause_texture.hide()
        self.add(self.pause_texture)

        pause_in_time = clutter.Timeline(1000)
        in_alpha_pause = clutter.Alpha(pause_in_time, clutter.EASE_IN_OUT_SINE)

        self.pause_in_opacity = clutter.BehaviourOpacity(alpha=in_alpha_pause,
                                                         opacity_start=100,
                                                         opacity_end=255)
        self.pause_in_scale = clutter.BehaviourScale(1.0, 1.0, 1.4, 1.4,
                                                     in_alpha_pause)

        self.pause_in_opacity.apply(self.pause_texture)
        self.pause_in_scale.apply(self.pause_texture)

        pause_out_time = clutter.Timeline(1000)
        out_alpha_pause = clutter.Alpha(pause_out_time,
                                        clutter.EASE_IN_OUT_SINE)

        self.pause_out_opacity = clutter.BehaviourOpacity(
            alpha=out_alpha_pause, opacity_start=255, opacity_end=100)
        self.pause_out_scale = clutter.BehaviourScale(1.4, 1.4, 1.0, 1.0,
                                                      out_alpha_pause)

        self.pause_out_opacity.apply(self.pause_texture)
        self.pause_out_scale.apply(self.pause_texture)

        self.score = clutter.Score()
        self.score.set_loop(True)
        self.score.append(timeline=pause_in_time)
        self.score.append(timeline=pause_out_time, parent=pause_in_time)
        self.score.start()

        self.seekbackward_texture = Texture(
            self.theme.getImage("media-seek-backward"), 0.1, 0.5)
        self.seekbackward_texture.set_anchor_point_from_gravity(
            clutter.GRAVITY_CENTER)
        self.seekbackward_texture.set_opacity(0)
        self.add(self.seekbackward_texture)

        self.seekbackward_timeline = clutter.Timeline(1000)
        alpha_seekbackward = clutter.Alpha(self.seekbackward_timeline,
                                           clutter.EASE_IN_OUT_SINE)

        self.seekbackward_opacity = clutter.BehaviourOpacity(
            alpha=alpha_seekbackward, opacity_start=255, opacity_end=0)
        self.seekbackward_opacity.apply(self.seekbackward_texture)

        self.seekforward_texture = Texture(
            self.theme.getImage("media-seek-forward"), 0.9, 0.5)
        self.seekforward_texture.set_anchor_point_from_gravity(
            clutter.GRAVITY_CENTER)
        self.seekforward_texture.set_opacity(0)
        self.add(self.seekforward_texture)

        self.seekforward_timeline = clutter.Timeline(1000)
        alpha_seekforward = clutter.Alpha(self.seekforward_timeline,
                                          clutter.EASE_IN_OUT_SINE)

        self.seekforward_opacity = clutter.BehaviourOpacity(
            alpha=alpha_seekforward, opacity_start=255, opacity_end=0)
        self.seekforward_opacity.apply(self.seekforward_texture)

    def _create_progress_bar(self):
        '''Create the progress bar.'''
        progress_bar = ProgressBar(0.5, 0.9, 0.40, 0.04)
        progress_bar.auto_display = True
        progress_bar.media_player = self.media_player
        progress_bar.visible = True
        return progress_bar

    def _create_aspect_ratio_textures(self):
        '''
        Create textures that which are displayed when user changes aspect ratio.
        '''
        texture_1 = Texture(self.theme.getImage("native_aspect_ratio"))
        texture_1.hide()
        self.add(texture_1)
        texture_2 = Texture(self.theme.getImage("widescreen_aspect_ratio"))
        texture_2.hide()
        self.add(texture_2)
        texture_3 = Texture(self.theme.getImage("zoom_aspect_ratio"))
        texture_3.hide()
        self.add(texture_3)
        texture_4 = Texture(self.theme.getImage("compromise_aspect_ratio"))
        texture_4.hide()
        self.add(texture_4)
        self.aspect_textures = [texture_1, texture_2, texture_3, texture_4]
        self.timeout_key = None  # This is used when canceling timeouts
        for texture in self.aspect_textures:
            texture.position = (
                float(self.config.stage_width - texture.get_width()) /
                (self.config.stage_width * 2), 0.67)

    def _hide_aspect_ratio_logo(self, number):
        '''
        Hide aspect ratio texture. This is a callback function and
        shouldn't be called directly.
        '''
        self.aspect_textures[number].hide()
        self.timeout_key = None
        return False

    def _display_aspect_ratio_logo(self, number):
        '''Display aspect ratio logo on screen when ratio is changed.'''
        if self.timeout_key is not None:
            gobject.source_remove(self.timeout_key)
        for texture in self.aspect_textures:
            texture.hide()
        self.aspect_textures[number].show()
        self.timeout_key = gobject.timeout_add(2000,
                                               self._hide_aspect_ratio_logo,
                                               number)

    def _handle_play_pause(self, event=None):
        '''Handle UserEvent.PLAYER_PLAY_PAUSE.'''
        if self.media_player.is_playing:
            self.pause_texture.hide()
        else:
            self.pause_texture.show()
        self._progress_bar.visible = True

    def _handle_skip_backward(self, event=None):
        '''Handle UserEvent.PLAYER_SKIP_BACKWARD.'''
        self._progress_bar.visible = True
        self.seekbackward_timeline.start()

    def _handle_skip_forward(self, event=None):
        '''Handle UserEvent.PLAYER_SKIP_FORWARD.'''
        self._progress_bar.visible = True
        self.seekforward_timeline.start()

    def _handle_stop(self):
        '''Handle UserEvent.PLAYER_STOP.'''
        self._progress_bar.visible = True
        self.pause_texture.hide()

    def _handle_ratio_1(self):
        '''Handle UserEvent.USE_ASPECT_RATIO_1.'''
        self._display_aspect_ratio_logo(0)

    def _handle_ratio_2(self):
        '''Handle UserEvent.USE_ASPECT_RATIO_2.'''
        self._display_aspect_ratio_logo(1)

    def _handle_ratio_3(self):
        '''Handle UserEvent.USE_ASPECT_RATIO_3.'''
        self._display_aspect_ratio_logo(2)

    def _handle_ratio_4(self):
        '''Handle UserEvent.USE_ASPECT_RATIO_4.'''
        self._display_aspect_ratio_logo(3)
示例#34
0
 def _create_aspect_ratio_textures(self):
     '''
     Create textures that which are displayed when user changes aspect ratio.
     '''
     texture_1 = Texture(self.theme.getImage("native_aspect_ratio"))
     texture_1.hide()
     self.add(texture_1)
     texture_2 = Texture(self.theme.getImage("widescreen_aspect_ratio"))
     texture_2.hide()
     self.add(texture_2)
     texture_3 = Texture(self.theme.getImage("zoom_aspect_ratio"))
     texture_3.hide()
     self.add(texture_3)
     texture_4 = Texture(self.theme.getImage("compromise_aspect_ratio"))
     texture_4.hide()
     self.add(texture_4)
     self.aspect_textures = [texture_1, texture_2, texture_3, texture_4]
     self.timeout_key = None # This is used when canceling timeouts
     for texture in self.aspect_textures:
         texture.position = (
             float(self.config.stage_width - texture.get_width()) /
             (self.config.stage_width * 2), 0.67)
示例#35
0
    def _create_navigation_textures(self):
        '''Create the pause, seek-backward & seek-forward textures.'''
        self.pause_texture = Texture(
            self.theme.getImage("media-playback-pause"), 0.5, 0.5)
        self.pause_texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
        self.pause_texture.hide()
        self.add(self.pause_texture)

        pause_in_time = clutter.Timeline(1000)
        in_alpha_pause = clutter.Alpha(pause_in_time, clutter.EASE_IN_OUT_SINE)

        self.pause_in_opacity = clutter.BehaviourOpacity(alpha=in_alpha_pause,
            opacity_start=100, opacity_end=255)
        self.pause_in_scale = clutter.BehaviourScale(1.0, 1.0, 1.4, 1.4,
            in_alpha_pause)

        self.pause_in_opacity.apply(self.pause_texture)
        self.pause_in_scale.apply(self.pause_texture)

        pause_out_time = clutter.Timeline(1000)
        out_alpha_pause = clutter.Alpha(pause_out_time,
            clutter.EASE_IN_OUT_SINE)

        self.pause_out_opacity = clutter.BehaviourOpacity(alpha=out_alpha_pause,
            opacity_start=255, opacity_end=100)
        self.pause_out_scale = clutter.BehaviourScale(1.4, 1.4, 1.0, 1.0,
            out_alpha_pause)

        self.pause_out_opacity.apply(self.pause_texture)
        self.pause_out_scale.apply(self.pause_texture)

        self.score = clutter.Score()
        self.score.set_loop(True)
        self.score.append(timeline=pause_in_time)
        self.score.append(timeline=pause_out_time, parent=pause_in_time)
        self.score.start()

        self.seekbackward_texture = Texture(
            self.theme.getImage("media-seek-backward"), 0.1, 0.5)
        self.seekbackward_texture.set_anchor_point_from_gravity(
            clutter.GRAVITY_CENTER)
        self.seekbackward_texture.set_opacity(0)
        self.add(self.seekbackward_texture)

        self.seekbackward_timeline = clutter.Timeline(1000)
        alpha_seekbackward = clutter.Alpha(self.seekbackward_timeline,
            clutter.EASE_IN_OUT_SINE)

        self.seekbackward_opacity = clutter.BehaviourOpacity(
            alpha=alpha_seekbackward, opacity_start=255, opacity_end=0)
        self.seekbackward_opacity.apply(self.seekbackward_texture)

        self.seekforward_texture = Texture(
            self.theme.getImage("media-seek-forward"), 0.9, 0.5)
        self.seekforward_texture.set_anchor_point_from_gravity(
            clutter.GRAVITY_CENTER)
        self.seekforward_texture.set_opacity(0)
        self.add(self.seekforward_texture)

        self.seekforward_timeline = clutter.Timeline(1000)
        alpha_seekforward = clutter.Alpha(self.seekforward_timeline,
            clutter.EASE_IN_OUT_SINE)

        self.seekforward_opacity = clutter.BehaviourOpacity(
            alpha=alpha_seekforward, opacity_start=255, opacity_end=0)
        self.seekforward_opacity.apply(self.seekforward_texture)
示例#36
0
class ImageMenu(GridMenu):
    """A grid menu that contains images."""
    __gsignals__ = {
        'filled': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
    }

    def __init__(self, x=0, y=0, item_width=0.2, item_height=0.1):
        GridMenu.__init__(self, x, y, item_width, item_height)

        self.cursor_below = False
        self.horizontal = True
        self.items_per_col = 4
        self.visible_rows = 3
        self.visible_cols = 5

        c = clutter.Rectangle()
        c.set_size(100, 100)
        c.set_color((255, 255, 255, 128))
        self.cursor = c

        pix_buffer = gtk.gdk.pixbuf_new_from_file(
            self.config.theme.getImage("default_movie_art"))
        self.movie_default = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer)
        self.movie_default.hide()
        self.add(self.movie_default)

        self.album_default = Texture(
            self.config.theme.getImage("default_album_art"))
        self.album_default.hide()
        self.add(self.album_default)

    def add_item(self, texture, data):
        """Add a ImageMenuItem from a Texture."""
        item = ImageMenuItem(self._item_width, self._item_height, texture)
        item.userdata = data

        self.raw_add_item(item)

    def async_add(self, items):
        """
        Add asynchronously ImageMenuItem using a list.

        The list should be : [[texture1, data1], [texture2, data2], etc]

        texture1, texture2 : are Texture objects.
        data1, data2: are the data that will be accessible from the menu item.
        (see MenuItem Class)
        """
        if len(items) > 0:
            item = items[0]
            self.add_item(item[0], item[1])

            # Recursive call, remove first element from the list
            gobject.timeout_add(15, self.async_add, items[1:])
        else:
            self.emit("filled")

        return False

    # XXX: This needs to be changed. An ImageMenu should know nothing about
    # special video items.
    def async_add_videos(self, items):
        """
        Add asynchronously ImageMenuItem using a list.
        The created ImageMenuItem fits movies and series requirements.
        See also async_add comments.
        """
        if len(items) > 0:
            item = items[0]
            if item[1].has_cover_art():
                pix_buffer = gtk.gdk.pixbuf_new_from_file(item[0])
                texture = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer)
            else:
                texture = clutter.Clone(self.movie_default)

            self.add_item(texture, item[1])

            # Recursive call, remove first element from the list
            gobject.timeout_add(10, self.async_add_videos, items[1:])
        else:
            self.emit("filled")

        return False

    # XXX: This needs to be changed. An ImageMenu should know nothing about
    # special album items.
    def async_add_albums(self, items):
        """
        Add asynchronously ImageMenuItem using a list.
        The created ImageMenuItem fits albums requirements.
        See also async_add comments.
        """
        if len(items) > 0:
            item = items[0]
            if item[1].has_album_art():
                texture = Texture(item[0])
            else:
                texture = clutter.Clone(self.album_default)

            self.add_item(texture, item[1])

            # Recursive call, remove first element from the list
            gobject.timeout_add(10, self.async_add_albums, items[1:])
        else:
            self.emit("filled")

        return False

    # XXX: This needs to be changed. An ImageMenu should know nothing about
    # special clip items.
    def async_add_clips(self, items):
        """
        Add asynchronously ImageMenuItem using a list.
        The created ImageMenuItem fits clips requirements.
        See also async_add comments.
        """
        if len(items) > 0:
            item = items[0]
            pix_buffer = gtk.gdk.pixbuf_new_from_file(item[0])
            texture = RoundedTexture(0.0, 0.0, 0.23,
                                     self.y_for_x(0.23) * 0.7, pix_buffer)

            self.add_item(texture, item[1])

            # Recursive call, remove first element from the list
            gobject.timeout_add(10, self.async_add_clips, items[1:])
        else:
            self.emit("filled")

        return False
示例#37
0
class Selector(clutter.Group):
    '''Selector is an animated texture that can be used to display current
    position in menus.'''
    def __init__(self, theme):
        clutter.Group.__init__(self)
        self.animate_selector = False

        selector_filename = theme.getImage("selector")
        glow_filename = theme.getImage("selector_glow")

        # Set selector base texture
        self.selector = Texture(selector_filename)
        self.selector.set_opacity(200)
        self.add(self.selector)

        # Set selector GLOW texture
        self.glow = Texture(glow_filename)
        self.glow.set_opacity(0)
        self.add(self.glow)

        # Animate selector (Glow effect with glow overlay texture)
        self.in_time = clutter.Timeline(1500)
        self.in_alpha = clutter.Alpha(self.in_time, clutter.EASE_IN_OUT_SINE)
        self.in_behaviour = clutter.BehaviourOpacity(0, 255, self.in_alpha)
        self.in_behaviour.apply(self.glow)

        self.out_time = clutter.Timeline(1500)
        self.out_alpha = clutter.Alpha(self.out_time, clutter.EASE_IN_OUT_SINE)
        self.out_behaviour = clutter.BehaviourOpacity(255, 0, self.out_alpha)
        self.out_behaviour.apply(self.glow)

        self.score = clutter.Score()
        self.score.set_loop(True)
        self.score.append(timeline=self.in_time)
        # Link the out Timeline so that there is a smooth fade out.
        self.score.append(timeline=self.out_time, parent=self.in_time)

    def set_size(self, width, height):
        """Overrides clutter.Actor.set_size()"""
        self.selector.set_size(width, height)
        self.glow.set_size(width, height)

    def stop_animation(self):
        """Stop selector animation"""
        self.score.stop()
        self.glow.set_opacity(0)

    def start_animation(self):
        """Start selector animation"""
        self.score.start()

    def hide(self):
        """Overrides clutter.Actor.hide()"""
        clutter.Group.hide(self)
        if self.animate_selector:
            self.stop_animation()

    def show(self):
        """Overrides clutter.Actor.show()"""
        clutter.Group.show(self)
        self.start_animation()

    def hide_all(self):
        """Overrides clutter.Actor.hide_all()"""
        clutter.Group.hide_all(self)
        self.stop_animation()

    def show_all(self):
        """Overrides clutter.Actor.show_all()"""
        clutter.Group.show_all(self)
        self.start_animation()
示例#38
0
 def _create_aspect_ratio_textures(self):
     '''
     Create textures that which are displayed when user changes aspect ratio.
     '''
     texture_1 = Texture(self.theme.getImage("native_aspect_ratio"))
     texture_1.hide()
     self.add(texture_1)
     texture_2 = Texture(self.theme.getImage("widescreen_aspect_ratio"))
     texture_2.hide()
     self.add(texture_2)
     texture_3 = Texture(self.theme.getImage("zoom_aspect_ratio"))
     texture_3.hide()
     self.add(texture_3)
     texture_4 = Texture(self.theme.getImage("compromise_aspect_ratio"))
     texture_4.hide()
     self.add(texture_4)
     self.aspect_textures = [texture_1, texture_2, texture_3, texture_4]
     self.timeout_key = None  # This is used when canceling timeouts
     for texture in self.aspect_textures:
         texture.position = (
             float(self.config.stage_width - texture.get_width()) /
             (self.config.stage_width * 2), 0.67)
示例#39
0
 def testNoInitPosition(self):
     """Test that a texture will be displayed at the origin of the display
     area if no initial position is given"""
     no_pos_texture = Texture(self.filename)
     self.assertEqual(no_pos_texture.position, (0, 0))
示例#40
0
    def _create_playing_preview(self):
        '''Create the Now Playing preview sidebar.'''
        preview = clutter.Group()

        # Video preview of current media
        video_texture = self.media_player.get_texture()
        if video_texture == None:
            video_texture = Texture(self.theme.getImage("default_album_art"))
        width, height = video_texture.get_size()
        x_ratio = (self.PREVIEW_WIDTH - 50) / float(width)
        y_ratio = (self.PREVIEW_HEIGHT - 50) / float(height)

        if x_ratio > y_ratio:
            video_texture.set_scale((self.PREVIEW_HEIGHT - 50) / float(height),
                                    (self.PREVIEW_HEIGHT - 50) / float(height))
            new_width = int(width * \
                ((self.PREVIEW_HEIGHT - 50) / float(height)))
            new_x = int(((self.PREVIEW_WIDTH - 50) - new_width) / 2.0)
            video_texture.set_position(int(new_x), 0)
            # Below are size and position calculations for border rectangle
            rect_x = new_x -3
            rect_y = -3
            new_width = (self.PREVIEW_HEIGHT - 50) / float(height) * width
            new_height = (self.PREVIEW_HEIGHT - 50) / float(height) * height
        else:
            video_texture.set_scale((self.PREVIEW_WIDTH - 50) / float(width),
                                    (self.PREVIEW_WIDTH - 50) / float(width))
            new_height = int(height * \
                ((self.PREVIEW_WIDTH - 50) / float(width)))
            new_y = int(((self.PREVIEW_HEIGHT - 50) - new_height) / 2.0)
            video_texture.set_position(0, int(new_y))
            rect_x = -3
            rect_y = new_y -3
            # Below are size and position calculations for border rectangle
            new_width = (self.PREVIEW_WIDTH - 50) / float(width) * width
            new_height = (self.PREVIEW_WIDTH - 50) / float(width) * height

        # Video frame
        rect = clutter.Rectangle()
        rect.set_size(int(new_width + 6), int(new_height + 6))
        rect.set_position(rect_x, rect_y)
        rect.set_color((128, 128, 128, 192))
        preview.add(rect)

        preview.add(video_texture)

        self._preview_title = Label(0.03, "text", 0.03, 0.74, "")
        preview.add(self._preview_title)

        return preview
示例#41
0
class Disc(Screen):
    '''Screen allows user to play tracks from the current Audio CD.'''

    def __init__(self, music_library, media_player):
        Screen.__init__(self, 'Disc')

        self.theme = self.config.theme
        self.music_library = music_library
        self.media_player = media_player
        # When album info is loaded we create Playlist object
        self.playlist = None

        self.art = None
        self.art2 = None
        self.in_behaviour = None
        self.out_behaviour = None
        self.li = None
        self.track_menu = None

        # Screen Title (Displayed at the bottom left corner)
        screen_title = Label(0.13, "screentitle", 0, 0.87, _("Audio Disc"),
            "screen_title")
        self.add(screen_title)

        # Display throbber animation while loading CD metadata
        self.throbber = LoadingAnimation(0.5, 0.5, 0.1)
        self.throbber.show()
        self.add(self.throbber)

        # Create and initialize screen items
        self.has_disc = True
        gobject.timeout_add(500, self._get_disc_information)

    def _get_disc_information(self):
        """
        Fetch album information from the Internet and create widgets based
        on received data.
        """
        try:
            disc = self.music_library.get_compact_disc_information()

            title = disc.title
            artist = disc.artist
            tracks = disc.tracks

            self.playlist = Playlist(tracks)
            self._create_album_info(title, artist, tracks, disc.length)
            self.track_menu = self._create_track_menu(tracks)
            self.add(self.track_menu)
            self._create_album_cover_texture(artist, title)

            self.li = ListIndicator(0.75, 0.8, 0.2, 0.045,
                ListIndicator.VERTICAL)
            self.li.set_maximum(len(tracks))
            self.add(self.li)

            art_file = os.path.join(self.config.ALBUM_ART_DIR,
                artist + " - " + title + ".jpg")
            if artist != "Unknown artist" and not os.path.exists(art_file):
                art_search = AlbumArtDownloader(title, artist,
                    self.config.ALBUM_ART_DIR, self._update_albumart)
                art_search.start()

        except cdrom.error:
            # No disc in drive
            self.has_disc = False
            no_disc = Label(0.05, "title", 0.5, 0.5,
                _("No audio disc in drive"))
            no_disc.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
            self.add(no_disc)

        # Remove loading animation
        self.throbber.hide()
        self.remove(self.throbber)
        del self.throbber

        # This function should be called only once (gobject timeout)
        return False

    def _update_albumart(self, artist, title):
        """
        Search album art for current audio disc. This function is called only
        if album art doesn't exist already. If album art is found then we
        replace current disc icon with the new album art.
        @param artist: Artist name
        @param title: Album title
        """
        art_file = os.path.join(self.config.ALBUM_ART_DIR,
            artist + " - " + title + ".jpg")

        if os.path.exists(art_file):
            clutter.threads_enter()
            self.art2 = Texture(art_file, 0.1, 0.165)
            clutter.threads_leave()

            self.art2.set_size(self.get_abs_x(0.3148), self.get_abs_y(0.5599))
            self.art2.set_opacity(0)
            self.add(self.art2)

            timeline_in = clutter.Timeline(35, 26)
            alpha_in = clutter.Alpha(timeline_in, clutter.smoothstep_inc_func)
            self.in_behaviour = clutter.BehaviourOpacity(0, 255, alpha_in)
            self.in_behaviour.apply(self.art2)

            timeline_out = clutter.Timeline(35, 26)
            alpha_out = clutter.Alpha(timeline_out, clutter.smoothstep_inc_func)
            self.out_behaviour = clutter.BehaviourOpacity(255, 0, alpha_out)
            self.out_behaviour.apply(self.art)

            timeline_out.start()
            timeline_in.start()

    def _create_album_cover_texture(self, artist, title):
        """
        Create a texture that is displayed next to track list. This texture
        displays album cover art.
        @param artist: Artist
        @param title: Title of the album
        """
        coverfile = os.path.join(self.config.ALBUM_ART_DIR,
            artist + " - " + title + ".jpg")

        if(os.path.exists(coverfile)):
            pixbuf = gtk.gdk.pixbuf_new_from_file(coverfile)
        else:
            pixbuf = gtk.gdk.pixbuf_new_from_file(self.theme.getImage("disc"))
        self.art = EyeCandyTexture(0.1, 0.13, 0.3148, 0.5599, pixbuf)
        self.art.set_rotation(clutter.Y_AXIS, 25, 0, 0, 0)
        self.add(self.art)

    def _create_album_info(self, title, artist_name, tracks, length):
        """
        Create album info labels.
        @param title: Album title
        @param artist_name: Artist
        @param tracks: List of CompactDisc objects
        """
        album = Label(0.04167, "text", 0.50146, 0.13,
           artist_name + " - " + title, font_weight="bold")
        album.set_size(0.4393, 0.06510)
        album.set_ellipsize(pango.ELLIPSIZE_END)
        self.add(album)

        minutes = str(length / 60)

        num_of_tracks = Label(0.02604, "subtitle", 0.50146, 0.18,
            _("%(total)s tracks, %(time)s minutes") % \
            {'total': len(tracks), 'time': minutes}, font_weight="bold")
        self.add(num_of_tracks)

    def _create_track_menu(self, tracks):
        """
        Create a track menu. This menu contains list of all tracks on album.
        @param tracks: List of CompactDisc objects
        """
        menu = TextMenu(0.4978, 0.2344, 0.4393, 0.0781)
        menu.visible_rows = 7

        tracks_list = [[track.title, track.length_string, index] \
            for index, track in enumerate(tracks)]
        menu.async_add(tracks_list)

        menu.active = True
        menu.connect('selected', self._handle_select)
        menu.connect('moved', self._display_selected_track)

        return menu

    def _handle_up(self):
        '''Handle UserEvent.NAVIGATE_UP.'''
        self.track_menu.up()

    def _handle_down(self):
        '''Handle UserEvent.NAVIGATE_DOWN.'''
        self.track_menu.down()

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        track_index = self.track_menu.selected_userdata
        self.playlist.set_current(track_index)
        self.media_player.set_playlist(self.playlist)
        self.media_player.play()

    def handle_user_event(self, event):
        '''Handle user events unless there is no disc.'''
        if self.has_disc:
            Screen.handle_user_event(self, event)

    def _display_selected_track(self, event=None):
        '''Update of the list indicator.'''
        self.li.set_current(self.track_menu.selected_index + 1)