class ScrollMenuItem(clutter.Group): """A Group containing a Label to which a LoopedPathBehaviour is applied.""" __gtype_name__ = 'ScrollMenuItem' def __init__(self, alpha, text, item_height, font_size, color_name): clutter.Group.__init__(self) self.label = Label(font_size, color_name, 0, 0) self.label.set_text(text) self.behaviour = LoopedPathBehaviour(alpha) self.behaviour.apply(self) self.add(self.label)
class ScrollMenuItem(clutter.Group): """A Group containing a Label to which a LoopedPathBehaviour is applied.""" __gtype_name__ = "ScrollMenuItem" def __init__(self, alpha, text, item_height, font_size, color_name): clutter.Group.__init__(self) self.label = Label(font_size, color_name, 0, 0) self.label.set_text(text) self.behaviour = LoopedPathBehaviour(alpha) self.behaviour.apply(self) self.add(self.label)
class Album(Screen): '''Screen that allows user to browse and play tracks of the music album.''' def __init__(self, media_player, music_library, move_to_new_screen_callback, album): Screen.__init__(self, 'Album', move_to_new_screen_callback) self.media_player = media_player self.theme = self.config.theme self.library = music_library self.album = album self.art = None self.track_menu = None # Create and initialize screen items self.track_menu = self._create_track_menu() self.add(self.track_menu) self._create_album_cover_texture() self._create_album_info() self.screen_title = Label(0.13, "screentitle", 0, 0.87, "") self.screen_title.set_ellipsize(pango.ELLIPSIZE_END) self.screen_title.width = 0.8 self.add(self.screen_title) #List indicator self.li = ListIndicator(0.74, 0.85, 0.2, 0.045, ListIndicator.VERTICAL) self.li.set_maximum(len(self.album.tracks)) self.add(self.li) self.track_menu.active = True self.track_menu.connect('selected', self._on_menu_selected) self.track_menu.connect('moved', self._display_selected_track) def _create_album_cover_texture(self): """ Create a texture that is displayed next to track list. This texture displays album cover art. """ if(self.album.has_album_art()): pixbuf = gtk.gdk.pixbuf_new_from_file(self.album.album_art_url) else: pixbuf = gtk.gdk.pixbuf_new_from_file( self.theme.getImage("default_album_art")) 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): """ Create album info labels. """ if self.album.year != 0: album_text = self.album.title + ", " + str(self.album.year) else: album_text = self.album.title album = Label(0.0416, "text", 0.5, 0.13, album_text, font_weight="bold") album.set_ellipsize(pango.ELLIPSIZE_END) album.set_line_wrap(False) album.width = 0.45 self.add(album) length = str(self.album.length / 60) num_of_tracks_text = _("%(total)s tracks, %(time)s minutes") % \ {'total': len(self.album.tracks), 'time': length} num_of_tracks = Label(0.028, "subtitle", 0.5, 0.18, num_of_tracks_text, font_weight="bold") self.add(num_of_tracks) def _create_track_menu(self): """ Create track menu. This menu contains list of all tracks on album. """ menu = TextMenu(0.4978, 0.2344, 0.4393, 0.0781) tracks = self.album.tracks tracks_list = [[track.title, track.length_string, track] \ for track in tracks] menu.async_add(tracks_list) return menu 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. """ track = self.track_menu.selected_userdata self.media_player.set_media(track) self.media_player.play() 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 = self.track_menu.selected_userdata kwargs = { 'track' : track } self.callback("audio_play", kwargs) def _on_menu_selected(self, actor=None): '''Handle a *select command* if an item was selected.''' self._handle_select() def _display_selected_track(self, actor=None): '''Update of the list indicator and the screen's title''' self.li.set_current(self.track_menu.selected_index + 1) track = self.track_menu.selected_userdata self.screen_title.set_text(track.artist) self.screen_title.show()
class VideoClipsTab(Tab): """ Tab can be used as part of the TabGroup Tab is a very simple container that contains all the widgets and logic of the tab page. """ def __init__(self, media_player, video_library, move_to_new_screen_callback, name="clips", tab_title=_("Video clips")): Tab.__init__(self, name, tab_title, move_to_new_screen_callback) self.media_player = media_player self.video_library = video_library self.theme = self.config.theme self.list_indicator = None self.clip_info = None self.menu = None self.clip_title = None if self.video_library.get_number_of_video_clips() == 0: self._create_empty_library_notice() else: # Start the loading animation while the menu is loading self.throbber = LoadingAnimation(0.7, 0.1) self.throbber.show() self.add(self.throbber) self.menu = self._create_menu() self.add(self.menu) self.menu.connect("moved", self._update_clip_info) self.menu.connect("selected", self._handle_select) self.menu.connect("activated", self._on_activated) self.menu.connect("filled", self._on_menu_filled) self.connect('activated', self._on_activated) self.connect('deactivated', self._on_deactivated) def can_activate(self): """ Allow if we have some movies indexed. """ if self.video_library.get_number_of_video_clips() == 0: return False else: return True def _create_empty_library_notice(self): """ Create an information box that is displayed if there are no indexed movies. """ message = _( "There are no indexed Video Clips in the Entertainer media " "library. Please add some folders containing video clips " "to the Library using the configuration tool.") Tab.show_empty_tab_notice(self, _("No video clips available!"), message) def _create_menu(self): """ Create a view that is displayed when there are indexed clips in the video library. """ menu = ImageMenu(0.04, 0.16, 0.23, self.y_for_x(0.23) * 0.7) menu.items_per_col = 2 menu.visible_rows = 2 menu.visible_cols = 4 clips = self.video_library.get_video_clips() clips_list = [[clip.thumbnail_url, clip] for clip in clips] menu.async_add_clips(clips_list) # Create list indicator self.list_indicator = ListIndicator(0.7, 0.8, 0.2, 0.045, ListIndicator.HORIZONTAL) self.list_indicator.set_maximum(len(clips)) self.list_indicator.show() self.add(self.list_indicator) # Create information labels self.clip_title = Label(0.042, "title", 0.15, 0.77, "", font_weight="bold") self.clip_title.set_ellipsize(pango.ELLIPSIZE_END) self.clip_title.set_line_wrap(False) self.clip_title.width = 0.5 self.add(self.clip_title) self.clip_info = Label(0.034, "subtitle", 0.15, 0.85, "") self.clip_info.set_ellipsize(pango.ELLIPSIZE_END) self.clip_info.set_line_wrap(False) self.clip_info.width = 0.5 self.add(self.clip_info) return menu def _update_clip_info(self, event=None): '''Update the VideoClip information labels.''' if self.active: clip = self.menu.selected_userdata (folder, filename) = os.path.split(clip.filename) self.clip_title.set_text(filename) self.clip_info.set_text(folder) self.list_indicator.show() self.list_indicator.set_current(self.menu.selected_index + 1) else: self.clip_title.set_text("") self.clip_info.set_text("") self.list_indicator.hide() def _handle_up(self): '''Handle the up user event.''' if self.menu.on_top: return True # Move control back to tab bar else: self.menu.up() return False def _handle_down(self): '''Handle the down user event.''' self.menu.down() return False def _handle_left(self): '''Handle the left user event.''' self.menu.left() return False def _handle_right(self): '''Handle the right user event.''' self.menu.right() return False def _handle_select(self, event=None): '''Handle the select user event.''' clip = self.menu.selected_userdata self.media_player.set_media(clip) self.media_player.play() self.callback("video_osd") return False def _on_activated(self, event=None): '''Tab activated.''' if self.tab_group is not None: self.tab_group.active = False self.menu.active = True self.active = True self._update_clip_info() return False def _on_deactivated(self, event=None): '''Tab deactivated.''' self.active = False self.menu.active = False self._update_clip_info() return False def _on_menu_filled(self, event=None): '''Handles filled event.''' self.throbber.hide()
class TvEpisodes(Screen): '''Screen contains list of all episodes of one specific season.''' def __init__(self, media_player, move_to_new_screen_callback, episodes, tv_series): Screen.__init__(self, 'TvEpisodes', move_to_new_screen_callback) self.episodes = episodes self.media_player = media_player self.theme = self.config.theme self.tv_series = tv_series # Screen Title (Displayed at the bottom left corner) screen_title = Label(0.13, "screentitle", 0, 0.87, self.tv_series.title) self.add(screen_title) self.scroll_area = None self.title = None self.thumb = None self.menu = self._create_episode_menu() self.add(self.menu) self._create_episode_info_box() #List indicator self.li = ListIndicator(0.8, 0.9, 0.2, 0.045, ListIndicator.VERTICAL) self.li.set_maximum(len(self.episodes)) self.add(self.li) self.menu.connect("moved", self._update_episode_info) self.menu.connect("selected", self._handle_select) self.menu.connect("activated", self._on_menu_activated) def _create_episode_menu(self): """Create a list of available seasons.""" menu = TextMenu(0.4978, 0.1563, 0.4393, 0.0781) episodes_list = [[_("%(num)d. %(title)s") % \ {'num': episode.number, 'title': episode.title}, None, episode] for episode in self.episodes] menu.async_add(episodes_list) menu.active = True return menu def _create_thumbnail_texture(self): """Create a thumbnail texture. This is called as menu is scrolled.""" if self.thumb: self.thumb.hide() # Thumbnail. Use cover art if thumbnail doesn't exist thumbnail = self.menu.selected_userdata.thumbnail_url if(thumbnail is not None): pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail) thumb_width = 0.2928 thumb_height = 0.2799 thumb_x = 0.05 thumb_y = 0.2 else: thumb_width = 0.1098 thumb_height = 0.2799 thumb_x = 0.20 thumb_y = 0.15 if(self.tv_series.has_cover_art()): pixbuf = gtk.gdk.pixbuf_new_from_file( self.tv_series.cover_art_url) else: pixbuf = gtk.gdk.pixbuf_new_from_file( self.theme.getImage("default_movie_art")) self.thumb = EyeCandyTexture(thumb_x, thumb_y, thumb_width, thumb_height, pixbuf) self.add(self.thumb) def _create_episode_info_box(self): """ Create a texture that is displayed next to track list. This texture displays album cover art. """ self._create_thumbnail_texture() # Title self.title = Label(0.04, "title", 0.05, 0.55, self.menu.selected_userdata.title, font_weight="bold") self.title.set_ellipsize(pango.ELLIPSIZE_END) self.title.set_line_wrap(False) self.title.width = 0.4 self.add(self.title) # Plot plot = Label(0.029, "subtitle", 0, 0, self.menu.selected_userdata.plot) plot.width = 0.4 self.scroll_area = ScrollArea(0.05, 0.63, 0.4, 0.15, plot) self.scroll_area.connect("activated", self._on_scroll_area_activated) self.add(self.scroll_area) def _update_episode_info(self, event=None): '''Update information from this episode.''' self.li.set_current(self.menu.selected_index + 1) self._create_thumbnail_texture() self.title.set_text(self.menu.selected_userdata.title) self.title.width = 0.4 plot = Label(0.029, "subtitle", 0, 0, self.menu.selected_userdata.plot) plot.width = 0.4 self.scroll_area.set_content(plot) def _handle_up(self): '''Handle UserEvent.NAVIGATE_UP.''' if self.menu.active: self.menu.up() else: self.scroll_area.scroll_up() def _handle_down(self): '''Handle UserEvent.NAVIGATE_DOWN.''' if self.menu.active: self.menu.down() else: self.scroll_area.scroll_down() def _handle_left(self): '''Handle UserEvent.NAVIGATE_LEFT.''' self.menu.active = False self.scroll_area.active = True def _handle_right(self): '''Handle UserEvent.NAVIGATE_RIGHT.''' self.menu.active = True self.scroll_area.active = False def _handle_select(self, event=None): '''Handle UserEvent.NAVIGATE_SELECT.''' episode = self.menu.selected_userdata self.media_player.set_media(episode) self.media_player.play() self.callback("video_osd") def _on_menu_activated(self, event=None): '''Handle menu activation.''' self.scroll_area.active = False def _on_scroll_area_activated(self, event=None): '''Handle scroll_area activation.''' self.menu.active = False
class Photographs(Screen): '''Screen displays a grid of selectable photograph thumbnails.''' 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) def _update_image_info(self, event=None): """Update image information box.""" image = self.images[self.menu.selected_index] name = image.get_title() desc = image.get_description() self.image_title.set_text(name) self.image_title.set_size(0.366, 0.04167) self.image_desc.set_text(desc) self.image_desc.set_size(0.366, 0.0911) self.li.set_current(self.menu.selected_index + 1) def _create_list_indicator(self): '''Create list indicator for albums list.''' self.li = ListIndicator(0.77, 0.8, 0.18, 0.045, ListIndicator.HORIZONTAL) self.li.set_maximum(len(self.images)) self.add(self.li) def _handle_up(self): '''Handle UserEvent.NAVIGATE_UP.''' self.menu.up() def _handle_down(self): '''Handle UserEvent.NAVIGATE_DOWN.''' self.menu.down() def _handle_left(self): '''Handle UserEvent.NAVIGATE_LEFT.''' self.menu.left() def _handle_right(self): '''Handle UserEvent.NAVIGATE_RIGHT.''' self.menu.right() def _handle_select(self, event=None): '''Handle UserEvent.NAVIGATE_SELECT.''' index = self.menu.selected_index kwargs = {'current_photo_index' : index, 'images' : self.images} self.callback("photo", kwargs) def _on_menu_filled(self, event=None): '''Handles filled event.''' self.throbber.hide()
class TextMenuItem(MenuItem): """A menuitem widget that contains one or two labels.""" def __init__(self, width, height, text, extra_text=None): MenuItem.__init__(self) self.width = width self.height = height self.theme = self.config.theme self.text = text self.extra_text = extra_text self.color = "menuitem_inactive" self.font_size = 0.03 self.label = Label(self.font_size, self.color, 0, 0, "", "text_label") self.add(self.label) # Set extra text self.extra_label = None if extra_text is not None: self.extra_label = Label(self.font_size, self.color, 0, 0, "", "text_label") self.add(self.extra_label) self.update(text, extra_text) def animate_in(self): """Set labels font-size and color when an item gets selected.""" self.font_size = 0.037 self.color = "menuitem_active" self.update() def animate_out(self): """Set labels font-size and color when an item gets unselected.""" self.font_size = 0.03 self.color = "menuitem_inactive" self.update() def update(self, text=None, extra_text=None): """Updates text and dimensions of a TextMenuItem.""" if text is None: text = self.text else: self.text = text if extra_text is None: extra_text = self.extra_text else: self.extra_text = extra_text try: first_line = text[:text.index('\n')] except ValueError: first_line = text self.label.font_size = self.font_size self.label.set_text(first_line) self.label.position = (0.01, (self.height - self.label.height) / 2) self.label.set_line_wrap(False) self.label.set_ellipsize(pango.ELLIPSIZE_END) # Set extra text if extra_text is not None: self.extra_label.font_size = self.font_size self.extra_label.set_text(extra_text) self.extra_label.position = ( self.width * 0.98 - self.extra_label.width, (self.height - self.extra_label.height) / 2) self.label.width = (self.width - self.extra_label.width) * 0.9 else: self.label.width = self.width * 0.95 self.label.color = self.color if self.extra_label: self.extra_label.color = self.color
class MoviesTab(Tab): """ Tab can be used as part of the TabGroup Tab is a very simple container that contains all the widgets and logic of the tab page. """ def __init__(self, video_library, move_to_new_screen_callback, name="movies", tab_title=_("Movies")): Tab.__init__(self, name, tab_title, move_to_new_screen_callback) self.video_library = video_library self.theme = self.config.theme self.list_indicator = None self.movie_info = None self.menu = None self.movie_plot = None self.movie_title = None if self.video_library.get_number_of_movies() == 0: self._create_empty_library_notice() else: # Start the loading animation while the menu is loading self.throbber = LoadingAnimation(0.1, 0.1) self.throbber.show() self.add(self.throbber) self.menu = self._create_menu() self.add(self.menu) self.menu.connect("moved", self._update_movie_info) self.menu.connect("selected", self._handle_select) self.menu.connect("activated", self._on_activated) self.menu.connect("filled", self._on_menu_filled) self.connect('activated', self._on_activated) self.connect('deactivated', self._on_deactivated) def can_activate(self): """ Allow if we have some movies indexed. """ if self.video_library.get_number_of_movies() == 0: return False else: return True def _create_empty_library_notice(self): """ Create an information box that is displayed if there are no indexed movies. """ message = _( "There are no indexed movies in Entertainer media library. To " "add movies, click on 'content' button on toolbar and open " "'videos' tab. Now click on 'add' button and select some folders " "which contains movie files.") Tab.show_empty_tab_notice(self, _("No movies available!"), message) def _create_menu(self): """ Create a view that is displayed when there is indexed movies in the video library. """ #Create movie menu menu = ImageMenu(0.06, 0.18, 0.12, 0.25) menu.items_per_col = 2 menu.visible_rows = 2 menu.visible_cols = 7 movies = self.video_library.get_movies() movies_list = [[movie.cover_art_url, movie] for movie in movies] menu.async_add_videos(movies_list) # Create list indicator self.list_indicator = ListIndicator(0.75, 0.76, 0.2, 0.045, ListIndicator.HORIZONTAL) self.list_indicator.set_maximum(len(movies)) self.show() self.add(self.list_indicator) # Create information labels self.movie_title = Label(0.042, "title", 0.2, 0.75, "", font_weight="bold") self.movie_title.set_ellipsize(pango.ELLIPSIZE_END) self.movie_title.set_line_wrap(False) self.movie_title.width = 0.5 self.add(self.movie_title) self.movie_info = Label(0.034, "subtitle", 0.2, 0.8, "") self.movie_info.set_ellipsize(pango.ELLIPSIZE_END) self.movie_info.set_line_wrap(False) self.movie_info.width = 0.5 self.add(self.movie_info) self.movie_plot = Label(0.025, "subtitle", 0.2, 0.85, "") self.movie_plot.width = 0.7 self.add(self.movie_plot) return menu def _update_movie_info(self, event=None): '''Update the movie information labels.''' if self.active: movie = self.menu.selected_userdata genres = movie.genres if len(genres) > 1: genre = genres[0] + "/" + genres[1] else: genre = genres[0] self.movie_title.set_text(_("%(title)s (%(year)s)") % \ {'title': movie.title, 'year': movie.year}) self.movie_info.set_text(_("%(min)d min, (%(genre)s)") % \ {'min': movie.runtime, 'genre': genre}) self.movie_plot.set_text(movie.short_plot) self.list_indicator.show() self.list_indicator.set_current(self.menu.selected_index + 1) else: self.movie_title.set_text("") self.movie_info.set_text("") self.movie_plot.set_text("") self.list_indicator.hide() def _handle_up(self): '''Handle the up user event.''' if self.menu.on_top: return True # Move control back to tab bar else: self.menu.up() return False def _handle_down(self): '''Handle the down user event.''' self.menu.down() return False def _handle_left(self): '''Handle the left user event.''' self.menu.left() return False def _handle_right(self): '''Handle the right user event.''' self.menu.right() return False def _handle_select(self, event=None): '''Handle the select user event.''' movie = self.menu.selected_userdata kwargs = { 'movie' : movie } self.callback("movie", kwargs) return False def _on_activated(self, event=None): '''Tab activated.''' if self.tab_group is not None: self.tab_group.active = False self.menu.active = True self.active = True self._update_movie_info() return False def _on_deactivated(self, event=None): '''Tab deactivated.''' self.active = False self.menu.active = False self._update_movie_info() return False def _on_menu_filled(self, event=None): '''Handles filled event.''' self.throbber.hide()
class TracksTab(Tab): '''Tab for the artist screen to show track listings''' def __init__(self, tracks, move_to_new_screen_callback, name="tracks", tab_title=_("Tracks")): Tab.__init__(self, name, tab_title, move_to_new_screen_callback) # Start the loading animation while the menu is loading self.throbber = LoadingAnimation(0.1, 0.1) self.throbber.show() self.add(self.throbber) self.menu = TextMenu(0.0586, 0.2083, 0.2928, 0.0781) self.menu.items_per_row = 3 self.menu.visible_rows = 7 self.menu.visible_cols = 3 self.menu.active = False self.menu.cursor = None self.add(self.menu) tracks_list = [[track.title, None, track] for track in tracks] self.menu.async_add_artists(tracks_list) self.track_title = Label(0.045, "title", 0.22, 0.79, "") self.track_title.set_ellipsize(pango.ELLIPSIZE_END) self.track_title.set_line_wrap(False) self.track_title.width = 0.366 self.add(self.track_title) self.track_number = Label(0.037, "subtitle", 0.22, 0.86, "") self.track_number.set_ellipsize(pango.ELLIPSIZE_END) self.track_number.set_line_wrap(False) self.track_number.width = 0.366 self.add(self.track_number) self.track_length = Label(0.037, "subtitle", 0.22, 0.91, "") self.add(self.track_length) self.li = ListIndicator(0.77, 0.8, 0.18, 0.045, ListIndicator.VERTICAL) self.li.set_maximum(len(tracks)) self.li.show() self.add(self.li) self.connect('activated', self._on_activated) self.connect('deactivated', self._on_deactivated) self.menu.connect("moved", self._update_track_info) self.menu.connect("selected", self._handle_select) self.menu.connect("activated", self._on_activated) self.menu.connect("filled", self._on_menu_filled) def can_activate(self): '''Tracks tab will always be created from an existing artist with at least one track.''' return True def _update_track_info(self, event=None): '''Update the track information labels''' if self.active: track = self.menu.selected_userdata self.track_title.set_text(track.title) self.track_length.set_text(track.length_string) self.track_number.set_text(_("Track %(track)d from %(album)s") % \ {'track': track.number, 'album': track.album.title}) self.li.show() self.li.set_current(self.menu.selected_index + 1) else: self.track_title.set_text("") self.track_length.set_text("") self.track_number.set_text("") self.li.hide() def _handle_up(self): '''Handle the up user event.''' if self.menu.on_top: return True # Move control back to tab bar else: self.menu.up() return False def _handle_down(self): '''Handle the down user event.''' self.menu.down() return False def _handle_left(self): '''Handle the left user event.''' self.menu.left() return False def _handle_right(self): '''Handle the right user event.''' self.menu.right() return False def _handle_select(self, event=None): '''Handle the select user event.''' track = self.menu.selected_userdata kwargs = { 'track' : track } self.callback("audio_play", kwargs) return False def _on_activated(self, event=None): '''Tab activated.''' if self.tab_group is not None: self.tab_group.active = False self.menu.active = True self.active = True self._update_track_info() return False def _on_deactivated(self, event=None): '''Tab deactivated.''' self.active = False self.menu.active = False self._update_track_info() return False def _on_menu_filled(self, event=None): '''Handles filled event.''' self.throbber.hide()
class ListIndicator(Base, clutter.Group): """ ListIndicator displays 'current / maximum' value label and arrows. """ # Direction # HORIZONTAL layout: ARROW_LEFT current / maximum ARROW_RIGHT # VERITCAL layout: current / maximum ARROW_UP ARROW_DOWN HORIZONTAL = 0 VERTICAL = 1 def __init__(self, x, y, width, height, direction): Base.__init__(self) clutter.Group.__init__(self) self.config = Configuration() # Size self.width = self.get_abs_x(width) self.height = self.get_abs_y(height) self.direction = direction self.delimiter = " | " self.current = 1 self.maximum = 1 self.theme = self.config.theme self.fg = self.theme.get_color("arrow_foreground") self.bg = self.theme.get_color("arrow_background") if direction == ListIndicator.VERTICAL: text_x_pos = width / 3 else: text_x_pos = width / 2 self.text = Label( height * 0.8, "text", text_x_pos, height / 2, str(self.maximum) + self.delimiter + str(self.maximum)) self.text.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER) self.add(self.text) # Create arrows and calculate positions on screen if direction == ListIndicator.VERTICAL: self.arrow1 = ArrowTexture(5 * width / 7, height / 2, height / 2, self.fg, self.bg, ArrowTexture.UP) self.arrow2 = ArrowTexture(6 * width / 7, height / 2, height / 2, self.fg, self.bg, ArrowTexture.DOWN) elif direction == ListIndicator.HORIZONTAL: self.arrow1 = ArrowTexture(height / 2, height / 2, height / 2, self.fg, self.bg, ArrowTexture.LEFT) self.arrow2 = ArrowTexture(6 * width / 7, height / 2, height / 2, self.fg, self.bg, ArrowTexture.RIGHT) self.add(self.arrow1) self.add(self.arrow2) self.set_position(self.get_abs_x(x), self.get_abs_y(y)) def set_current(self, value): """ Set current value @param number: Current value (between 1 - maximum) """ if value > 0 and value <= self.maximum: self.current = value self._update_text() # Bounce arrow if user has reached the limit if self.current == 1: self.arrow1.bounce() elif self.current == self.maximum: self.arrow2.bounce() def get_current(self): """ Get current value @return Current value (integer) """ return self.current def set_maximum(self, value): """ Set maximum value of the indicator @param number: Set maximum value """ self.maximum = value self._update_text() def get_maximum(self): """ Get maximum value. @return Max value (integer) """ return self.maximum def set_delimiter(self, delimiter): """ Set delimiter text that is displayed between current and maximum value. Default delimiter text is ' / ' spaces included. @param delimiter: delimiter text """ self.delimiter = delimiter self._update_text() def show_position(self): """ Show position text that indicates the current position of the content. """ self.text.show() def hide_position(self): """ Hide position text that indicates the current position of the content. If this is called then indicator shows only arrows. """ self.text.hide() def _update_text(self): """Update the text label""" self.text.set_text( str(self.current) + self.delimiter + str(self.maximum))
class Main(Screen): '''Screen displayed when frontend is opened and provides main navigation.''' # Size of the preview area PREVIEW_WIDTH = 830 PREVIEW_HEIGHT = 580 # Directions UP = 0 DOWN = 1 def __init__(self, media_player, move_to_new_screen_callback): Screen.__init__(self, 'Main', move_to_new_screen_callback) self.media_player = media_player self.media_player.connect("stop", self.update) self.media_player.connect("play", self.update) self.media_player.connect('refresh', self._update_preview_title) self.theme = self.config.theme self._preview_title = None self.preview = clutter.Group() # Group that contains preview actors self.preview.set_position(self.get_abs_x(0.07), self.get_abs_y(0.1)) self.preview.show() self.preview.set_opacity(0x00) self.add(self.preview) self.menu = self._create_main_menu() self.add(self.menu) self._update_preview_area() self.add(ClockLabel(0.13, "screentitle", 0, 0.87)) self.menu.connect('selected', self._handle_select) self.menu.connect('moved', self._on_menu_moved) self.menu.active = True def get_type(self): """Return screen type.""" return Screen.NORMAL def get_name(self): """Return screen name (human readble).""" return "Main" def _create_main_menu(self): """Create main menu of the home screen.""" menu = ScrollMenu(10, 60, 0.045, "menuitem_active") menu.set_name("mainmenu") menu.add_item(_("Play CD"), "disc") menu.add_item(_("Videos"), "videos") menu.add_item(_("Music"), "music") menu.add_item(_("Photographs"), "photo") if self.config.display_weather_in_client: menu.add_item(_("Weather"), "weather") if self.media_player.has_media(): menu.add_item(_("Playing now..."), "playing") menu.visible_items = 5 menu.selected_index = 2 # Menu position menu_clip = menu.visible_items * 70 menu_y = int((self.config.stage_height - menu_clip + 10) / 2) menu.set_position(self.get_abs_x(0.75), menu_y) return menu 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 def _update_preview_title(self, event=None): '''Update the label showing the currently playing media.''' if self._preview_title != None: title_text = _("Now Playing: %(title)s (%(pos)s/%(length)s)") % \ {'title': self.media_player.get_media_title(), 'pos': self.media_player.get_media_position_string(), 'length': self.media_player.get_media_duration_string()} self._preview_title.set_text(title_text) def _update_preview_area(self): '''Update the preview area to display the current menu item.''' self.preview.remove_all() item = self.menu.get_selected() self.preview.set_opacity(0x00) update = True if item.get_name() == "playing": self.preview.add(self._create_playing_preview()) else: update = False # If the preview was updated fade it in. if update: fade_in = clutter.Timeline(500) alpha_in = clutter.Alpha(fade_in, clutter.EASE_IN_OUT_SINE) self.behaviour = clutter.BehaviourOpacity(0, 255, alpha_in) self.behaviour.apply(self.preview) fade_in.start() def update(self, event=None): """ Update screen widgets. This is called always when screen is poped from the screen history. Updates main menu widget. """ if self.media_player.is_playing and \ (self.menu.get_index("playing") == -1): self.menu.add_item(_("Playing now..."), "playing") elif not self.media_player.is_playing and \ (self.menu.get_index("playing") != -1): self.menu.remove_item("playing") self.menu.refresh() self._update_preview_area() def _handle_up(self): '''Handle UserEvent.NAVIGATE_UP.''' self.menu.scroll_up() def _handle_down(self): '''Handle UserEvent.NAVIGATE_DOWN.''' self.menu.scroll_down() def _handle_select(self, event=None): '''Handle UserEvent.NAVIGATE_SELECT.''' item = self.menu.get_selected() if item.get_name() == "disc": self.callback("audio_cd") elif item.get_name() == "playing": self.callback("video_osd") elif item.get_name() == "music": self.callback("music") elif item.get_name() == "videos": self.callback("video") elif item.get_name() == "weather": self.callback("weather") elif item.get_name() == "photo": self.callback("photo_albums") def _on_menu_moved(self, event): '''Update preview area when selected item changed on the menu.''' self._update_preview_area()
class PlayingTab(Tab): '''Tab for the audio play screen to show currently playing audio.''' def __init__(self, media_player, track, name="playing", tab_title=_("Currently playing")): Tab.__init__(self, name, tab_title) album = track.album # Track name if track.number == 0: track_label_text = track.title else: track_label_text = _("From %(number)d. %(title)s") % \ {'number': track.number, 'title': track.title} self.track_label = Label(0.05, "text", 0.5, 0.33, track_label_text) self.track_label.set_ellipsize(pango.ELLIPSIZE_END) self.track_label.width = 0.4 self.add(self.track_label) # Album name if album.year == 0: album_label_text = _("From %(title)s") % {'title': album.title} else: album_label_text = _("From %(title)s, %(year)s") % \ {'title': album.title, 'year': album.year} self.album_label = Label(0.042, "subtitle", 0.5, 0.43, album_label_text) self.album_label.set_ellipsize(pango.ELLIPSIZE_END) self.album_label.width = 0.4 self.add(self.album_label) # Artist name artist_text = _("By %(artist)s") % {'artist': track.artist} self.artist_label = Label(0.042, "subtitle", 0.5, 0.53, artist_text) self.artist_label.set_ellipsize(pango.ELLIPSIZE_END) self.artist_label.width = 0.4 self.add(self.artist_label) self.progress_bar = ProgressBar(0.5, 0.667, 0.4, 0.04) self.progress_bar.media_player = media_player self.add(self.progress_bar) def update(self, track): ''' Called when currently playing changes tracks. The provided track is used to update all the necessary labels. ''' album = track.album if track.number == 0: self.track_label.set_text(track.title) else: self.track_label.set_text(str(track.number) + ". " + track.title) if album.year != 0: self.album_label.set_text(_("From %(title)s, %(year)s") % \ {'title': album.title, 'year': album.year}) else: self.album_label.set_text(_("From %(album)s") % \ {'album': album.title}) self.artist_label.set_text(_("By %(artist)s") % \ {'artist': track.artist}) def can_activate(self): '''No interaction is available on the PlayingTab.''' return False
class ListIndicator(Base, clutter.Group): """ ListIndicator displays 'current / maximum' value label and arrows. """ # Direction # HORIZONTAL layout: ARROW_LEFT current / maximum ARROW_RIGHT # VERITCAL layout: current / maximum ARROW_UP ARROW_DOWN HORIZONTAL = 0 VERTICAL = 1 def __init__(self, x, y, width, height, direction): Base.__init__(self) clutter.Group.__init__(self) self.config = Configuration() # Size self.width = self.get_abs_x(width) self.height = self.get_abs_y(height) self.direction = direction self.delimiter = " | " self.current = 1 self.maximum = 1 self.theme = self.config.theme self.fg = self.theme.get_color("arrow_foreground") self.bg = self.theme.get_color("arrow_background") if direction == ListIndicator.VERTICAL: text_x_pos = width / 3 else: text_x_pos = width / 2 self.text = Label(height * 0.8, "text", text_x_pos, height / 2, str(self.maximum) + self.delimiter + str(self.maximum)) self.text.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER) self.add(self.text) # Create arrows and calculate positions on screen if direction == ListIndicator.VERTICAL: self.arrow1 = ArrowTexture(5 * width / 7, height / 2, height / 2, self.fg, self.bg, ArrowTexture.UP) self.arrow2 = ArrowTexture(6 * width / 7, height / 2, height / 2, self.fg, self.bg, ArrowTexture.DOWN) elif direction == ListIndicator.HORIZONTAL: self.arrow1 = ArrowTexture(height / 2, height / 2, height / 2, self.fg, self.bg, ArrowTexture.LEFT) self.arrow2 = ArrowTexture(6 * width / 7, height / 2, height / 2, self.fg, self.bg, ArrowTexture.RIGHT) self.add(self.arrow1) self.add(self.arrow2) self.set_position(self.get_abs_x(x), self.get_abs_y(y)) def set_current(self, value): """ Set current value @param number: Current value (between 1 - maximum) """ if value > 0 and value <= self.maximum: self.current = value self._update_text() # Bounce arrow if user has reached the limit if self.current == 1: self.arrow1.bounce() elif self.current == self.maximum: self.arrow2.bounce() def get_current(self): """ Get current value @return Current value (integer) """ return self.current def set_maximum(self, value): """ Set maximum value of the indicator @param number: Set maximum value """ self.maximum = value self._update_text() def get_maximum(self): """ Get maximum value. @return Max value (integer) """ return self.maximum def set_delimiter(self, delimiter): """ Set delimiter text that is displayed between current and maximum value. Default delimiter text is ' / ' spaces included. @param delimiter: delimiter text """ self.delimiter = delimiter self._update_text() def show_position(self): """ Show position text that indicates the current position of the content. """ self.text.show() def hide_position(self): """ Hide position text that indicates the current position of the content. If this is called then indicator shows only arrows. """ self.text.hide() def _update_text(self): """Update the text label""" self.text.set_text( str(self.current) + self.delimiter + str(self.maximum))
class AlbumsTab(Tab): '''Tab to show album listings''' def __init__(self, albums, move_to_new_screen_callback, name="albums", tab_title=_("Albums")): Tab.__init__(self, name, tab_title, move_to_new_screen_callback) # Start the loading animation while the menu is loading self.throbber = LoadingAnimation(0.6, 0.1) self.throbber.show() self.add(self.throbber) if len(albums) < 4: x_percent = 0.2928 visible_rows = 1 visible_cols = 3 elif len(albums) < 13: x_percent = 0.1464 visible_rows = 2 visible_cols = 6 else: x_percent = 0.1098 visible_rows = 3 visible_cols = 8 # Create albums menu self.menu = ImageMenu(0.07, 0.16, x_percent, self.y_for_x(x_percent)) self.menu.visible_rows = visible_rows self.menu.visible_cols = visible_cols self.menu.items_per_col = self.menu.visible_rows self.add(self.menu) albums_list = [[album.album_art_url, album] for album in albums] self.menu.async_add_albums(albums_list) self.li = ListIndicator(0.77, 0.8, 0.18, 0.045, ListIndicator.HORIZONTAL) self.li.set_maximum(len(albums)) self.li.show() self.add(self.li) # Create album information (displays current menuitem information) self.album_title = Label(0.045, "title", 0.22, 0.79, "") self.album_title.set_ellipsize(pango.ELLIPSIZE_END) self.album_title.set_line_wrap(False) self.album_title.width = 0.366 self.add(self.album_title) self.album_artist = Label(0.037, "subtitle", 0.22, 0.86, "") self.album_artist.set_ellipsize(pango.ELLIPSIZE_END) self.album_artist.set_line_wrap(False) self.album_artist.width = 0.366 self.add(self.album_artist) self.album_tracks = Label(0.037, "subtitle", 0.22, 0.91, "") self.add(self.album_tracks) self.connect('activated', self._on_activated) self.connect('deactivated', self._on_deactivated) self.menu.connect("moved", self._update_album_info) self.menu.connect("selected", self._handle_select) self.menu.connect("activated", self._on_activated) self.menu.connect("filled", self._on_menu_filled) def can_activate(self): '''Albums tab will always be created from an existing artist with at least one album.''' return True def _update_album_info(self, event=None): '''Update the album information labels.''' if self.active: album = self.menu.selected_userdata self.album_title.set_text(album.title) self.album_artist.set_text(album.artist) self.album_tracks.set_text(_("%(total)s tracks") % \ {'total': len(album.tracks)}) self.li.show() self.li.set_current(self.menu.selected_index + 1) else: self.album_title.set_text("") self.album_artist.set_text("") self.album_tracks.set_text("") self.li.hide() def _handle_up(self): '''Handle the up user event.''' if self.menu.on_top: return True # Move control back to tab bar else: self.menu.up() return False def _handle_down(self): '''Handle the down user event.''' self.menu.down() return False def _handle_left(self): '''Handle the left user event.''' self.menu.left() return False def _handle_right(self): '''Handle the right user event.''' self.menu.right() return False def _handle_select(self, event=None): '''Handle the select user event.''' album = self.menu.selected_userdata kwargs = { 'album' : album } self.callback("album", kwargs) return False def _on_activated(self, event=None): '''Tab activated.''' if self.tab_group is not None: self.tab_group.active = False self.menu.active = True self.active = True self._update_album_info() return False def _on_deactivated(self, event=None): '''Tab deactivated.''' self.active = False self.menu.active = False self._update_album_info() return False def _on_menu_filled(self, event=None): '''Handles filled event.''' self.throbber.hide()
class TvEpisodes(Screen): '''Screen contains list of all episodes of one specific season.''' def __init__(self, media_player, move_to_new_screen_callback, episodes, tv_series): Screen.__init__(self, 'TvEpisodes', move_to_new_screen_callback) self.episodes = episodes self.media_player = media_player self.theme = self.config.theme self.tv_series = tv_series # Screen Title (Displayed at the bottom left corner) screen_title = Label(0.13, "screentitle", 0, 0.87, self.tv_series.title) self.add(screen_title) self.scroll_area = None self.title = None self.thumb = None self.menu = self._create_episode_menu() self.add(self.menu) self._create_episode_info_box() #List indicator self.li = ListIndicator(0.8, 0.9, 0.2, 0.045, ListIndicator.VERTICAL) self.li.set_maximum(len(self.episodes)) self.add(self.li) self.menu.connect("moved", self._update_episode_info) self.menu.connect("selected", self._handle_select) self.menu.connect("activated", self._on_menu_activated) def _create_episode_menu(self): """Create a list of available seasons.""" menu = TextMenu(0.4978, 0.1563, 0.4393, 0.0781) episodes_list = [[_("%(num)d. %(title)s") % \ {'num': episode.number, 'title': episode.title}, None, episode] for episode in self.episodes] menu.async_add(episodes_list) menu.active = True return menu def _create_thumbnail_texture(self): """Create a thumbnail texture. This is called as menu is scrolled.""" if self.thumb: self.thumb.hide() # Thumbnail. Use cover art if thumbnail doesn't exist thumbnail = self.menu.selected_userdata.thumbnail_url if (thumbnail is not None): pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail) thumb_width = 0.2928 thumb_height = 0.2799 thumb_x = 0.05 thumb_y = 0.2 else: thumb_width = 0.1098 thumb_height = 0.2799 thumb_x = 0.20 thumb_y = 0.15 if (self.tv_series.has_cover_art()): pixbuf = gtk.gdk.pixbuf_new_from_file( self.tv_series.cover_art_url) else: pixbuf = gtk.gdk.pixbuf_new_from_file( self.theme.getImage("default_movie_art")) self.thumb = EyeCandyTexture(thumb_x, thumb_y, thumb_width, thumb_height, pixbuf) self.add(self.thumb) def _create_episode_info_box(self): """ Create a texture that is displayed next to track list. This texture displays album cover art. """ self._create_thumbnail_texture() # Title self.title = Label(0.04, "title", 0.05, 0.55, self.menu.selected_userdata.title, font_weight="bold") self.title.set_ellipsize(pango.ELLIPSIZE_END) self.title.set_line_wrap(False) self.title.width = 0.4 self.add(self.title) # Plot plot = Label(0.029, "subtitle", 0, 0, self.menu.selected_userdata.plot) plot.width = 0.4 self.scroll_area = ScrollArea(0.05, 0.63, 0.4, 0.15, plot) self.scroll_area.connect("activated", self._on_scroll_area_activated) self.add(self.scroll_area) def _update_episode_info(self, event=None): '''Update information from this episode.''' self.li.set_current(self.menu.selected_index + 1) self._create_thumbnail_texture() self.title.set_text(self.menu.selected_userdata.title) self.title.width = 0.4 plot = Label(0.029, "subtitle", 0, 0, self.menu.selected_userdata.plot) plot.width = 0.4 self.scroll_area.set_content(plot) def _handle_up(self): '''Handle UserEvent.NAVIGATE_UP.''' if self.menu.active: self.menu.up() else: self.scroll_area.scroll_up() def _handle_down(self): '''Handle UserEvent.NAVIGATE_DOWN.''' if self.menu.active: self.menu.down() else: self.scroll_area.scroll_down() def _handle_left(self): '''Handle UserEvent.NAVIGATE_LEFT.''' self.menu.active = False self.scroll_area.active = True def _handle_right(self): '''Handle UserEvent.NAVIGATE_RIGHT.''' self.menu.active = True self.scroll_area.active = False def _handle_select(self, event=None): '''Handle UserEvent.NAVIGATE_SELECT.''' episode = self.menu.selected_userdata self.media_player.set_media(episode) self.media_player.play() self.callback("video_osd") def _on_menu_activated(self, event=None): '''Handle menu activation.''' self.scroll_area.active = False def _on_scroll_area_activated(self, event=None): '''Handle scroll_area activation.''' self.menu.active = False
class ArtistsTab(Tab): '''Tab for the music screen to show artist listings''' def __init__(self, music_library, artists, move_to_new_screen_callback, name="artists", tab_title=_("Artists")): Tab.__init__(self, name, tab_title, move_to_new_screen_callback) self.library = music_library # Start the loading animation while the menu is loading self.throbber = LoadingAnimation(0.1, 0.1) self.throbber.show() self.add(self.throbber) self.menu = TextMenu(0.057, 0.208, 0.293, 0.078) self.menu.items_per_row = 3 self.menu.visible_rows = 7 self.menu.visible_cols = 3 self.menu.active = False self.menu.cursor = None self.add(self.menu) artists_list = [[artist, None, artist] for artist in artists] self.menu.async_add_artists(artists_list) # Create artist label self.artist_title = Label(0.0416, "title", 0.22, 0.794, "") self.artist_title.set_ellipsize(pango.ELLIPSIZE_END) self.artist_title.set_line_wrap(False) self.artist_title.width = 0.366 self.add(self.artist_title) self.artist_albums = Label(0.0365, "subtitle", 0.22, 0.86, "") self.add(self.artist_albums) self.artist_tracks = Label(0.0365, "subtitle", 0.22, 0.911, "") self.add(self.artist_tracks) # Create artist menu list indicator self.li = ListIndicator(0.77, 0.8, 0.18, 0.045, ListIndicator.VERTICAL) self.li.set_maximum(len(artists)) self.add(self.li) self.connect('activated', self._on_activated) self.connect('deactivated', self._on_deactivated) self.menu.connect("moved", self._update_artist_info) self.menu.connect("selected", self._handle_select) self.menu.connect("activated", self._on_activated) self.menu.connect("filled", self._on_menu_filled) def can_activate(self): '''Albums tab will always be created from an existing artist with at least one album.''' return True def _update_artist_info(self, event=None): '''Update the artist information labels''' if self.active: artist = self.menu.selected_userdata self.artist_title.set_text(artist) self.artist_albums.set_text(_("%(albums)d albums") % {'albums': self.library.number_of_albums_by_artist(artist)}) self.artist_tracks.set_text(_("%(tracks)d tracks") % {'tracks': self.library.number_of_tracks_by_artist(artist)}) self.li.show() self.li.set_current(self.menu.selected_index + 1) else: self.artist_title.set_text("") self.artist_albums.set_text("") self.artist_tracks.set_text("") self.li.hide() def _handle_up(self): '''Handle the up user event.''' if self.menu.on_top: return True # Move control back to tab bar else: self.menu.up() return False def _handle_down(self): '''Handle the down user event.''' self.menu.down() return False def _handle_left(self): '''Handle the left user event.''' self.menu.left() return False def _handle_right(self): '''Handle the right user event.''' self.menu.right() return False def _handle_select(self, event=None): '''Handle the select user event.''' artist = self.menu.selected_userdata kwargs = { 'artist' : artist } self.callback("artist", kwargs) return False def _on_activated(self, event=None): '''Tab activated.''' if self.tab_group is not None: self.tab_group.active = False self.menu.active = True self.active = True self._update_artist_info() return False def _on_deactivated(self, event=None): '''Tab deactivated.''' self.active = False self.menu.active = False self._update_artist_info() def _on_menu_filled(self, event=None): '''Handles filled event.''' self.throbber.hide()
class ProgressBar(Base, clutter.Group): ''' Progress bar widget. This widget implements simple progress bar. ''' # Cursor's radius in % of the widget's height CURSOR_RADIUS = 0.3 BAR_LENGTH = 0.5 INFO_LENGTH = (1 - BAR_LENGTH) / 2.0 def __init__(self, x, y, width, height, color="title"): Base.__init__(self) clutter.Group.__init__(self) self.width = self.get_abs_x(width) self.height = self.get_abs_y(height) self.bar_width = int(self.width * self.BAR_LENGTH) self.bar_x = int(self.width * self.INFO_LENGTH) self.media_length_x = (1 - self.INFO_LENGTH + 0.05) * width self.set_position(self.get_abs_x(x), self.get_abs_y(y)) self._color = self._color_to_cairo_color( self.config.theme.get_color(color)) self._background = clutter.CairoTexture(self.bar_width, self.height) self._draw_background() self._background.set_position(self.bar_x, 0) self.add(self._background) self._foreground = clutter.CairoTexture(self.height, self.height) self._foreground.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER) self._draw_foreground() self._foreground.set_position(self.bar_x, 0) self.add(self._foreground) self.media_position = Label(0.037, "title", 0, 0, "") self.add(self.media_position) self.media_length = Label(0.037, "title", self.media_length_x, 0, "") self.add(self.media_length) self._media_player = None self._progress_bar_moving = False self._hide_timeout_key = None self.auto_display = False self._visible = None self._timeline = clutter.Timeline(500) self._alpha = clutter.Alpha(self._timeline, clutter.EASE_IN_OUT_SINE) self._behaviour = clutter.BehaviourOpacity(0, 255, self._alpha) self._behaviour.apply(self) self._progress = None # Call the property setter to initialize the displayed position. self.progress = 0 # Preparation to pointer events handling. self._motion_handler = 0 self.set_reactive(True) self.connect('scroll-event', self._on_scroll_event) self.connect('button-press-event', self._on_button_press_event) self.connect('button-release-event', self._on_button_release_event) self.connect('enter-event', self._on_enter_event) self.connect('leave-event', self._on_leave_event) def _get_media_player(self): '''media_player property getter.''' return self._media_player def _set_media_player(self, media_player): '''media_player property setter.''' self._media_player = media_player self._media_player.connect('refresh', self._update_media_position) self._media_player.connect('stop', self._update_media_position) self._media_player.connect('position-changed', self._on_player_position_changed) media_player = property(_get_media_player, _set_media_player) def _get_progress(self): '''progress property getter.''' return self._progress def _set_progress(self, progress): '''progress property setter.''' if progress > 1: progress = 1 if progress < 0: progress = 0 self._progress = progress self._foreground.set_position(int(self.bar_x + (self.height / 2) + int(self.bar_width - self.height) * progress), int(self.height / 2)) progress = property(_get_progress, _set_progress) def _get_visible(self): '''visible property getter.''' if self._visible == None: self._visible = True return self._visible def _set_visible(self, boolean): '''visible property setter.''' if self._visible == boolean: return self._visible = boolean if boolean: self._timeline.set_direction(clutter.TIMELINE_FORWARD) self._timeline.rewind() self._timeline.start() if self.auto_display: if self._hide_timeout_key is not None: gobject.source_remove(self._hide_timeout_key) self._hide_timeout_key = gobject.timeout_add(3000, self._hide_progress_bar) else: self._timeline.set_direction(clutter.TIMELINE_BACKWARD) self._timeline.start() visible = property(_get_visible, _set_visible) def _hide_progress_bar(self): '''Update the progress bar.''' self.visible = False return False def _reset_auto_display_timeout(self): '''Reset the timeout if auto_display = True.''' if self._hide_timeout_key is not None: gobject.source_remove(self._hide_timeout_key) self._hide_timeout_key = gobject.timeout_add(3000, self._hide_progress_bar) def _update_media_position(self, event=None): '''Update the media's position.''' if not self._progress_bar_moving: self.progress = self.media_player.get_media_position() self.media_position.set_text( self.media_player.get_media_position_string()) self.media_length.set_text( self.media_player.get_media_duration_string()) def _draw_background(self): '''Draw background graphics.''' context = self._background.cairo_create() context.set_line_width(self.height * 0.09) context.set_source_rgba( self._color[0], self._color[1], self._color[2], 0.7) context.arc(self.height / 2, self.height / 2, self.height / 2 -2, math.pi / 2, math.pi * 1.5) context.arc(self.bar_width - self.height / 2, self.height / 2, self.height / 2 -2, math.pi * 1.5, math.pi / 2) context.close_path() context.stroke() del context def _draw_foreground(self): '''Draw foreground graphics.''' context = self._foreground.cairo_create() context.scale(self.height, self.height) context.set_source_rgba( self._color[0], self._color[1], self._color[2], 1.0) context.arc(0.5, 0.5, self.CURSOR_RADIUS, 0, 2 * math.pi) context.fill() del context def _color_to_cairo_color(self, color): '''Transform color to cairo format (0-255 to 0-1).''' (int_r, int_g, int_b, int_a) = color r = float(int_r) / 255.0 g = float(int_g) / 255.0 b = float(int_b) / 255.0 a = float(int_a) / 255.0 return r, g, b, a def _on_button_press_event(self, actor, event): '''button-press-event handler.''' if not self.visible: self.visible = True return clutter.grab_pointer(self) x = event.x - self.get_x() y = event.y - self.get_y() dx = x - int(self.bar_x + (self.height / 2) + \ (self.bar_width - self.height) * self._progress) dy = y - int(self.height / 2) # Calculation of the distance between our click and the middle of # the progress_bar cursor position. distance = math.sqrt(dx * dx + dy * dy) if distance <= (self.CURSOR_RADIUS * self.height): # Clicked around the cursor. if not self.handler_is_connected(self._motion_handler): self._motion_handler = self.connect('motion-event', self._on_motion_event) self._progress_bar_moving = True else: # Clicked far from the cursor. Change to the pointed position. progress = (x - self.bar_x - (self.height / 2.0)) / \ (self.bar_width - self.height) self.progress = progress self._progress_bar_moving = False self.media_player.set_media_position(self.progress) if self._hide_timeout_key is not None: gobject.source_remove(self._hide_timeout_key) return False def _on_button_release_event(self, actor, event): '''button-release-event handler.''' clutter.ungrab_pointer() if self.handler_is_connected(self._motion_handler): self.disconnect_by_func(self._on_motion_event) self._progress_bar_moving = False self.media_player.set_media_position(self.progress) if self.auto_display and self.visible: self._hide_timeout_key = gobject.timeout_add(3000, self._hide_progress_bar) return False def _on_motion_event(self, actor, event): '''motion-event handler.''' x_cursor = event.x - self.get_x() progress = (x_cursor - self.bar_x - (self.height / 2.0)) / \ (self.bar_width - self.height) self.progress = progress self.media_player.set_media_position(self.progress) return False def _on_scroll_event(self, actor, event): '''scroll-event handler (mouse's wheel).''' # +/- 2% per scroll event on the position of the media stream. self.visible = True scroll_progress_ratio = 0.02 if event.direction == clutter.SCROLL_DOWN: self.progress -= scroll_progress_ratio else: self.progress += scroll_progress_ratio self._progress_bar_moving = False self.media_player.set_media_position(self.progress) return False def _on_enter_event(self, stage, clutter_event): '''Shows the progress bar.''' if self.auto_display and not self._progress_bar_moving: self.visible = True def _on_leave_event(self, stage, clutter_event): '''Hides the progress bar.''' if self.auto_display and not self._progress_bar_moving: self.visible = False def _on_player_position_changed(self, event=None): '''Shows the progress bar.''' if self.auto_display and not self._progress_bar_moving: self.visible = True self._reset_auto_display_timeout()
class Photographs(Screen): '''Screen displays a grid of selectable photograph thumbnails.''' 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) def _update_image_info(self, event=None): """Update image information box.""" image = self.images[self.menu.selected_index] name = image.get_title() desc = image.get_description() self.image_title.set_text(name) self.image_title.set_size(0.366, 0.04167) self.image_desc.set_text(desc) self.image_desc.set_size(0.366, 0.0911) self.li.set_current(self.menu.selected_index + 1) def _create_list_indicator(self): '''Create list indicator for albums list.''' self.li = ListIndicator(0.77, 0.8, 0.18, 0.045, ListIndicator.HORIZONTAL) self.li.set_maximum(len(self.images)) self.add(self.li) def _handle_up(self): '''Handle UserEvent.NAVIGATE_UP.''' self.menu.up() def _handle_down(self): '''Handle UserEvent.NAVIGATE_DOWN.''' self.menu.down() def _handle_left(self): '''Handle UserEvent.NAVIGATE_LEFT.''' self.menu.left() def _handle_right(self): '''Handle UserEvent.NAVIGATE_RIGHT.''' self.menu.right() def _handle_select(self, event=None): '''Handle UserEvent.NAVIGATE_SELECT.''' index = self.menu.selected_index kwargs = {'current_photo_index': index, 'images': self.images} self.callback("photo", kwargs) def _on_menu_filled(self, event=None): '''Handles filled event.''' self.throbber.hide()
class AlbumsTab(Tab): '''Tab to show album listings''' def __init__(self, albums, move_to_new_screen_callback, name="albums", tab_title=_("Albums")): Tab.__init__(self, name, tab_title, move_to_new_screen_callback) # Start the loading animation while the menu is loading self.throbber = LoadingAnimation(0.6, 0.1) self.throbber.show() self.add(self.throbber) if len(albums) < 4: x_percent = 0.2928 visible_rows = 1 visible_cols = 3 elif len(albums) < 13: x_percent = 0.1464 visible_rows = 2 visible_cols = 6 else: x_percent = 0.1098 visible_rows = 3 visible_cols = 8 # Create albums menu self.menu = ImageMenu(0.07, 0.16, x_percent, self.y_for_x(x_percent)) self.menu.visible_rows = visible_rows self.menu.visible_cols = visible_cols self.menu.items_per_col = self.menu.visible_rows self.add(self.menu) albums_list = [[album.album_art_url, album] for album in albums] self.menu.async_add_albums(albums_list) self.li = ListIndicator(0.77, 0.8, 0.18, 0.045, ListIndicator.HORIZONTAL) self.li.set_maximum(len(albums)) self.li.show() self.add(self.li) # Create album information (displays current menuitem information) self.album_title = Label(0.045, "title", 0.22, 0.79, "") self.album_title.set_ellipsize(pango.ELLIPSIZE_END) self.album_title.set_line_wrap(False) self.album_title.width = 0.366 self.add(self.album_title) self.album_artist = Label(0.037, "subtitle", 0.22, 0.86, "") self.album_artist.set_ellipsize(pango.ELLIPSIZE_END) self.album_artist.set_line_wrap(False) self.album_artist.width = 0.366 self.add(self.album_artist) self.album_tracks = Label(0.037, "subtitle", 0.22, 0.91, "") self.add(self.album_tracks) self.connect('activated', self._on_activated) self.connect('deactivated', self._on_deactivated) self.menu.connect("moved", self._update_album_info) self.menu.connect("selected", self._handle_select) self.menu.connect("activated", self._on_activated) self.menu.connect("filled", self._on_menu_filled) def can_activate(self): '''Albums tab will always be created from an existing artist with at least one album.''' return True def _update_album_info(self, event=None): '''Update the album information labels.''' if self.active: album = self.menu.selected_userdata self.album_title.set_text(album.title) self.album_artist.set_text(album.artist) self.album_tracks.set_text(_("%(total)s tracks") % \ {'total': len(album.tracks)}) self.li.show() self.li.set_current(self.menu.selected_index + 1) else: self.album_title.set_text("") self.album_artist.set_text("") self.album_tracks.set_text("") self.li.hide() def _handle_up(self): '''Handle the up user event.''' if self.menu.on_top: return True # Move control back to tab bar else: self.menu.up() return False def _handle_down(self): '''Handle the down user event.''' self.menu.down() return False def _handle_left(self): '''Handle the left user event.''' self.menu.left() return False def _handle_right(self): '''Handle the right user event.''' self.menu.right() return False def _handle_select(self, event=None): '''Handle the select user event.''' album = self.menu.selected_userdata kwargs = {'album': album} self.callback("album", kwargs) return False def _on_activated(self, event=None): '''Tab activated.''' if self.tab_group is not None: self.tab_group.active = False self.menu.active = True self.active = True self._update_album_info() return False def _on_deactivated(self, event=None): '''Tab deactivated.''' self.active = False self.menu.active = False self._update_album_info() return False def _on_menu_filled(self, event=None): '''Handles filled event.''' self.throbber.hide()
class ProgressBar(Base, clutter.Group): ''' Progress bar widget. This widget implements simple progress bar. ''' # Cursor's radius in % of the widget's height CURSOR_RADIUS = 0.3 BAR_LENGTH = 0.5 INFO_LENGTH = (1 - BAR_LENGTH) / 2.0 def __init__(self, x, y, width, height, color="title"): Base.__init__(self) clutter.Group.__init__(self) self.width = self.get_abs_x(width) self.height = self.get_abs_y(height) self.bar_width = int(self.width * self.BAR_LENGTH) self.bar_x = int(self.width * self.INFO_LENGTH) self.media_length_x = (1 - self.INFO_LENGTH + 0.05) * width self.set_position(self.get_abs_x(x), self.get_abs_y(y)) self._color = self._color_to_cairo_color( self.config.theme.get_color(color)) self._background = clutter.CairoTexture(self.bar_width, self.height) self._draw_background() self._background.set_position(self.bar_x, 0) self.add(self._background) self._foreground = clutter.CairoTexture(self.height, self.height) self._foreground.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER) self._draw_foreground() self._foreground.set_position(self.bar_x, 0) self.add(self._foreground) self.media_position = Label(0.037, "title", 0, 0, "") self.add(self.media_position) self.media_length = Label(0.037, "title", self.media_length_x, 0, "") self.add(self.media_length) self._media_player = None self._progress_bar_moving = False self._hide_timeout_key = None self.auto_display = False self._visible = None self._timeline = clutter.Timeline(500) self._alpha = clutter.Alpha(self._timeline, clutter.EASE_IN_OUT_SINE) self._behaviour = clutter.BehaviourOpacity(0, 255, self._alpha) self._behaviour.apply(self) self._progress = None # Call the property setter to initialize the displayed position. self.progress = 0 # Preparation to pointer events handling. self._motion_handler = 0 self.set_reactive(True) self.connect('scroll-event', self._on_scroll_event) self.connect('button-press-event', self._on_button_press_event) self.connect('button-release-event', self._on_button_release_event) self.connect('enter-event', self._on_enter_event) self.connect('leave-event', self._on_leave_event) def _get_media_player(self): '''media_player property getter.''' return self._media_player def _set_media_player(self, media_player): '''media_player property setter.''' self._media_player = media_player self._media_player.connect('refresh', self._update_media_position) self._media_player.connect('stop', self._update_media_position) self._media_player.connect('position-changed', self._on_player_position_changed) media_player = property(_get_media_player, _set_media_player) def _get_progress(self): '''progress property getter.''' return self._progress def _set_progress(self, progress): '''progress property setter.''' if progress > 1: progress = 1 if progress < 0: progress = 0 self._progress = progress self._foreground.set_position( int(self.bar_x + (self.height / 2) + int(self.bar_width - self.height) * progress), int(self.height / 2)) progress = property(_get_progress, _set_progress) def _get_visible(self): '''visible property getter.''' if self._visible == None: self._visible = True return self._visible def _set_visible(self, boolean): '''visible property setter.''' if self._visible == boolean: return self._visible = boolean if boolean: self._timeline.set_direction(clutter.TIMELINE_FORWARD) self._timeline.rewind() self._timeline.start() if self.auto_display: if self._hide_timeout_key is not None: gobject.source_remove(self._hide_timeout_key) self._hide_timeout_key = gobject.timeout_add( 3000, self._hide_progress_bar) else: self._timeline.set_direction(clutter.TIMELINE_BACKWARD) self._timeline.start() visible = property(_get_visible, _set_visible) def _hide_progress_bar(self): '''Update the progress bar.''' self.visible = False return False def _reset_auto_display_timeout(self): '''Reset the timeout if auto_display = True.''' if self._hide_timeout_key is not None: gobject.source_remove(self._hide_timeout_key) self._hide_timeout_key = gobject.timeout_add(3000, self._hide_progress_bar) def _update_media_position(self, event=None): '''Update the media's position.''' if not self._progress_bar_moving: self.progress = self.media_player.get_media_position() self.media_position.set_text( self.media_player.get_media_position_string()) self.media_length.set_text( self.media_player.get_media_duration_string()) def _draw_background(self): '''Draw background graphics.''' context = self._background.cairo_create() context.set_line_width(self.height * 0.09) context.set_source_rgba(self._color[0], self._color[1], self._color[2], 0.7) context.arc(self.height / 2, self.height / 2, self.height / 2 - 2, math.pi / 2, math.pi * 1.5) context.arc(self.bar_width - self.height / 2, self.height / 2, self.height / 2 - 2, math.pi * 1.5, math.pi / 2) context.close_path() context.stroke() del context def _draw_foreground(self): '''Draw foreground graphics.''' context = self._foreground.cairo_create() context.scale(self.height, self.height) context.set_source_rgba(self._color[0], self._color[1], self._color[2], 1.0) context.arc(0.5, 0.5, self.CURSOR_RADIUS, 0, 2 * math.pi) context.fill() del context def _color_to_cairo_color(self, color): '''Transform color to cairo format (0-255 to 0-1).''' (int_r, int_g, int_b, int_a) = color r = float(int_r) / 255.0 g = float(int_g) / 255.0 b = float(int_b) / 255.0 a = float(int_a) / 255.0 return r, g, b, a def _on_button_press_event(self, actor, event): '''button-press-event handler.''' if not self.visible: self.visible = True return clutter.grab_pointer(self) x = event.x - self.get_x() y = event.y - self.get_y() dx = x - int(self.bar_x + (self.height / 2) + \ (self.bar_width - self.height) * self._progress) dy = y - int(self.height / 2) # Calculation of the distance between our click and the middle of # the progress_bar cursor position. distance = math.sqrt(dx * dx + dy * dy) if distance <= (self.CURSOR_RADIUS * self.height): # Clicked around the cursor. if not self.handler_is_connected(self._motion_handler): self._motion_handler = self.connect('motion-event', self._on_motion_event) self._progress_bar_moving = True else: # Clicked far from the cursor. Change to the pointed position. progress = (x - self.bar_x - (self.height / 2.0)) / \ (self.bar_width - self.height) self.progress = progress self._progress_bar_moving = False self.media_player.set_media_position(self.progress) if self._hide_timeout_key is not None: gobject.source_remove(self._hide_timeout_key) return False def _on_button_release_event(self, actor, event): '''button-release-event handler.''' clutter.ungrab_pointer() if self.handler_is_connected(self._motion_handler): self.disconnect_by_func(self._on_motion_event) self._progress_bar_moving = False self.media_player.set_media_position(self.progress) if self.auto_display and self.visible: self._hide_timeout_key = gobject.timeout_add( 3000, self._hide_progress_bar) return False def _on_motion_event(self, actor, event): '''motion-event handler.''' x_cursor = event.x - self.get_x() progress = (x_cursor - self.bar_x - (self.height / 2.0)) / \ (self.bar_width - self.height) self.progress = progress self.media_player.set_media_position(self.progress) return False def _on_scroll_event(self, actor, event): '''scroll-event handler (mouse's wheel).''' # +/- 2% per scroll event on the position of the media stream. self.visible = True scroll_progress_ratio = 0.02 if event.direction == clutter.SCROLL_DOWN: self.progress -= scroll_progress_ratio else: self.progress += scroll_progress_ratio self._progress_bar_moving = False self.media_player.set_media_position(self.progress) return False def _on_enter_event(self, stage, clutter_event): '''Shows the progress bar.''' if self.auto_display and not self._progress_bar_moving: self.visible = True def _on_leave_event(self, stage, clutter_event): '''Hides the progress bar.''' if self.auto_display and not self._progress_bar_moving: self.visible = False def _on_player_position_changed(self, event=None): '''Shows the progress bar.''' if self.auto_display and not self._progress_bar_moving: self.visible = True self._reset_auto_display_timeout()
class MoviesTab(Tab): """ Tab can be used as part of the TabGroup Tab is a very simple container that contains all the widgets and logic of the tab page. """ def __init__(self, video_library, move_to_new_screen_callback, name="movies", tab_title=_("Movies")): Tab.__init__(self, name, tab_title, move_to_new_screen_callback) self.video_library = video_library self.theme = self.config.theme self.list_indicator = None self.movie_info = None self.menu = None self.movie_plot = None self.movie_title = None if self.video_library.get_number_of_movies() == 0: self._create_empty_library_notice() else: # Start the loading animation while the menu is loading self.throbber = LoadingAnimation(0.1, 0.1) self.throbber.show() self.add(self.throbber) self.menu = self._create_menu() self.add(self.menu) self.menu.connect("moved", self._update_movie_info) self.menu.connect("selected", self._handle_select) self.menu.connect("activated", self._on_activated) self.menu.connect("filled", self._on_menu_filled) self.connect('activated', self._on_activated) self.connect('deactivated', self._on_deactivated) def can_activate(self): """ Allow if we have some movies indexed. """ if self.video_library.get_number_of_movies() == 0: return False else: return True def _create_empty_library_notice(self): """ Create an information box that is displayed if there are no indexed movies. """ message = _( "There are no indexed movies in Entertainer media library. To " "add movies, click on 'content' button on toolbar and open " "'videos' tab. Now click on 'add' button and select some folders " "which contains movie files.") Tab.show_empty_tab_notice(self, _("No movies available!"), message) def _create_menu(self): """ Create a view that is displayed when there is indexed movies in the video library. """ #Create movie menu menu = ImageMenu(0.06, 0.18, 0.12, 0.25) menu.items_per_col = 2 menu.visible_rows = 2 menu.visible_cols = 7 movies = self.video_library.get_movies() movies_list = [[movie.cover_art_url, movie] for movie in movies] menu.async_add_videos(movies_list) # Create list indicator self.list_indicator = ListIndicator(0.75, 0.76, 0.2, 0.045, ListIndicator.HORIZONTAL) self.list_indicator.set_maximum(len(movies)) self.show() self.add(self.list_indicator) # Create information labels self.movie_title = Label(0.042, "title", 0.2, 0.75, "", font_weight="bold") self.movie_title.set_ellipsize(pango.ELLIPSIZE_END) self.movie_title.set_line_wrap(False) self.movie_title.width = 0.5 self.add(self.movie_title) self.movie_info = Label(0.034, "subtitle", 0.2, 0.8, "") self.movie_info.set_ellipsize(pango.ELLIPSIZE_END) self.movie_info.set_line_wrap(False) self.movie_info.width = 0.5 self.add(self.movie_info) self.movie_plot = Label(0.025, "subtitle", 0.2, 0.85, "") self.movie_plot.width = 0.7 self.add(self.movie_plot) return menu def _update_movie_info(self, event=None): '''Update the movie information labels.''' if self.active: movie = self.menu.selected_userdata genres = movie.genres if len(genres) > 1: genre = genres[0] + "/" + genres[1] else: genre = genres[0] self.movie_title.set_text(_("%(title)s (%(year)s)") % \ {'title': movie.title, 'year': movie.year}) self.movie_info.set_text(_("%(min)d min, (%(genre)s)") % \ {'min': movie.runtime, 'genre': genre}) self.movie_plot.set_text(movie.short_plot) self.list_indicator.show() self.list_indicator.set_current(self.menu.selected_index + 1) else: self.movie_title.set_text("") self.movie_info.set_text("") self.movie_plot.set_text("") self.list_indicator.hide() def _handle_up(self): '''Handle the up user event.''' if self.menu.on_top: return True # Move control back to tab bar else: self.menu.up() return False def _handle_down(self): '''Handle the down user event.''' self.menu.down() return False def _handle_left(self): '''Handle the left user event.''' self.menu.left() return False def _handle_right(self): '''Handle the right user event.''' self.menu.right() return False def _handle_select(self, event=None): '''Handle the select user event.''' movie = self.menu.selected_userdata kwargs = {'movie': movie} self.callback("movie", kwargs) return False def _on_activated(self, event=None): '''Tab activated.''' if self.tab_group is not None: self.tab_group.active = False self.menu.active = True self.active = True self._update_movie_info() return False def _on_deactivated(self, event=None): '''Tab deactivated.''' self.active = False self.menu.active = False self._update_movie_info() return False def _on_menu_filled(self, event=None): '''Handles filled event.''' self.throbber.hide()