def __init__(self, listeners, util): """ Initializer :param util: utility object :param listener: screen menu event listener """ self.util = util self.config = util.config Container.__init__(self, util, background=(0, 0, 0)) self.factory = Factory(util) self.bounding_box = self.config[SCREEN_RECT] layout = BorderLayout(self.bounding_box) k = self.bounding_box.w/self.bounding_box.h percent_menu_width = (100.0 - PERCENT_TOP_HEIGHT - PERCENT_BOTTOM_HEIGHT)/k panel_width = (100.0 - percent_menu_width)/2.0 layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, panel_width, panel_width) self.genres = util.load_menu(GENRE_ITEMS, GENRE) self.current_genre = self.genres[self.config[CURRENT][PLAYLIST]] self.items_per_line = self.items_per_line(layout.CENTER.w) self.playlist = Playlist(self.config[CURRENT][LANGUAGE], self.current_genre.genre, util, self.items_per_line) font_size = (layout.TOP.h * PERCENT_TITLE_FONT)/100.0 color_dark = self.config[COLORS][COLOR_DARK] color_contrast = self.config[COLORS][COLOR_CONTRAST] self.screen_title = self.factory.create_dynamic_text("station_screen_title", layout.TOP, color_dark, color_contrast, int(font_size)) Container.add_component(self, self.screen_title) self.station_menu = StationMenu(self.playlist, util, (0, 0, 0), layout.CENTER) self.screen_title.set_text(self.station_menu.button.state.l_name) Container.add_component(self, self.station_menu) self.create_left_panel(layout, listeners) self.create_right_panel(layout, listeners) self.home_button.add_release_listener(listeners["go home"]) self.genres_button.add_release_listener(listeners["go genres"]) self.shutdown_button.add_release_listener(listeners["shutdown"]) self.left_button.add_release_listener(self.station_menu.switch_to_previous_station) self.left_button.add_release_listener(self.update_arrow_button_labels) self.page_down_button.add_release_listener(self.station_menu.switch_to_previous_page) self.page_down_button.add_release_listener(self.update_arrow_button_labels) self.right_button.add_release_listener(self.station_menu.switch_to_next_station) self.right_button.add_release_listener(self.update_arrow_button_labels) self.page_up_button.add_release_listener(self.station_menu.switch_to_next_page) self.page_up_button.add_release_listener(self.update_arrow_button_labels) self.station_menu.add_listener(listeners["play"]) self.station_menu.add_listener(self.screen_title.set_state) self.station_menu.add_listener(self.update_arrow_button_labels) self.station_menu.add_mode_listener(self.mode_listener) self.volume = self.factory.create_volume_control(layout.BOTTOM) self.volume.add_slide_listener(listeners["set volume"]) self.volume.add_slide_listener(listeners["set config volume"]) self.volume.add_slide_listener(listeners["set screensaver volume"]) self.volume.add_knob_listener(listeners["mute"]) Container.add_component(self, self.volume)
class Screen(Container): """ Base class for all screens. Extends Container class """ def __init__(self, util, title_key): """ Initializer :param util: utility object :param title_key: the resource bundle key for the screen title """ Container.__init__(self, util) self.util = util factory = Factory(util) config = util.config self.bounding_box = config[SCREEN_RECT] self.bgr = (0, 0, 0) self.layout = BorderLayout(config[SCREEN_RECT]) self.layout.set_percent_constraints(PERCENT_TOP_HEIGHT, 0, 0, 0) font_size = (self.layout.TOP.h * PERCENT_TITLE_FONT)/100.0 d = config[COLORS][COLOR_DARK] c = config[COLORS][COLOR_CONTRAST] self.screen_title = factory.create_output_text("screen_title", self.layout.TOP, d, c, int(font_size)) label = config[LABELS][title_key] self.screen_title.set_text(label) self.add_component(self.screen_title) def add_menu(self, menu): """ Add menu to the screen :param menu: the menu to add """ self.menu = menu self.add_component(menu) def get_clickable_rect(self): """ Return the list of rectangles which define the clickable areas on screen. Used for web browser. :return: list of rectangles """ c = Component(self.util) c.name = "clickable_rect" c.content = self.menu.bounding_box c.bgr = c.fgr = (0, 0, 0) c.content_x = c.content_y = 0 d = [c] return d
def create_left_panel(self, layout, listeners): """ Create Station Screen left panel. Include Shutdown button, Left button and Home button. :param layout: left panel layout :param listeners: event listeners """ panel_layout = BorderLayout(layout.LEFT) panel_layout.set_percent_constraints(PERCENT_SIDE_BOTTOM_HEIGHT, PERCENT_SIDE_BOTTOM_HEIGHT, 0, 0) left = self.station_menu.button.state.index self.left_button = self.factory.create_left_button(panel_layout.CENTER, str(left)) self.page_down_button = self.factory.create_page_down_button(panel_layout.CENTER, str(left)) self.page_down_button.set_visible(False) self.shutdown_button = self.factory.create_shutdown_button(panel_layout.TOP) self.home_button = self.factory.create_home_button(panel_layout.BOTTOM) panel = Container(self.util, layout.LEFT) panel.add_component(self.shutdown_button) panel.add_component(self.left_button) panel.add_component(self.page_down_button) panel.add_component(self.home_button) Container.add_component(self, panel)
def create_right_panel(self, layout, listeners): """ Create Station Screen right panel. Include Genre button, right button and Play/Pause button :param layout: right panel layout :param listeners: event listeners """ panel_layout = BorderLayout(layout.RIGHT) panel_layout.set_percent_constraints(PERCENT_SIDE_BOTTOM_HEIGHT, PERCENT_SIDE_BOTTOM_HEIGHT, 0, 0) self.genres_button = self.factory.create_genre_button(panel_layout.TOP, self.current_genre) right = self.playlist.length - self.station_menu.button.state.index - 1 self.right_button = self.factory.create_right_button(panel_layout.CENTER, str(right)) self.page_up_button = self.factory.create_page_up_button(panel_layout.CENTER, str(right)) self.page_up_button.set_visible(False) self.play_button = self.factory.create_play_pause_button(panel_layout.BOTTOM, listeners["play-pause"]) panel = Container(self.util, layout.RIGHT) panel.add_component(self.genres_button) panel.add_component(self.right_button) panel.add_component(self.page_up_button) panel.add_component(self.play_button) Container.add_component(self, panel)
class CdTracksScreen(Screen): """ File Browser Screen """ def __init__(self, util, listeners, voice_assistant, state): """ Initializer :param util: utility object :param listeners: file browser listeners """ self.util = util self.config = util.config self.cdutil = CdUtil(util) self.factory = Factory(util) self.bounding_box = self.config[SCREEN_RECT] self.layout = BorderLayout(self.bounding_box) self.layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, 0, 0) Screen.__init__(self, util, "", PERCENT_TOP_HEIGHT, voice_assistant, "cd_tracks_screen_title", True, self.layout.TOP) color_dark_light = self.config[COLORS][COLOR_DARK_LIGHT] self.current_cd_drive_name = self.config[CD_PLAYBACK][CD_DRIVE_NAME] self.current_cd_drive_id = self.config[CD_PLAYBACK][CD_DRIVE_ID] self.filelist = self.get_filelist() self.file_menu = FileMenu(self.filelist, util, None, (0, 0, 0), self.layout.CENTER, align=ALIGN_LEFT) self.go_cd_player = listeners[KEY_PLAYER] self.file_menu.add_play_file_listener(self.play_track) Container.add_component(self, self.file_menu) listeners[GO_LEFT_PAGE] = self.file_menu.page_down listeners[GO_RIGHT_PAGE] = self.file_menu.page_up listeners[KEY_EJECT] = self.eject_cd listeners[KEY_REFRESH] = self.refresh_tracks connected_cd_drives = self.cdutil.get_cd_drives_number() self.navigator = CdTracksNavigator(util, connected_cd_drives, self.layout.BOTTOM, listeners, color_dark_light) Container.add_component(self, self.navigator) self.file_menu.add_left_number_listener( self.navigator.left_button.change_label) self.file_menu.add_right_number_listener( self.navigator.right_button.change_label) self.file_menu.update_buttons() self.page_turned = False def play_track(self, state): """ Set config and go to player :param state: state object with track info """ self.config[CD_PLAYBACK][CD_DRIVE_ID] = self.current_cd_drive_id self.config[CD_PLAYBACK][CD_DRIVE_NAME] = self.current_cd_drive_name self.config[CD_PLAYBACK][CD_TRACK_TIME] = "0" if self.current_cd_drive_name != self.screen_title.text: state.album = self.screen_title.text self.go_cd_player(state) def get_filelist(self): """ Return CD tracks :return: page with CD tracks """ rows = 5 columns = 2 layout = GridLayout(self.layout.CENTER) layout.set_pixel_constraints(rows, columns, 1, 1) constr = layout.get_next_constraints() fixed_height = int((constr.h * 35) / 100.0) tracks = self.cdutil.get_cd_tracks(rows, columns, fixed_height, self.current_cd_drive_name) if tracks == None: tracks = [] return Page(tracks, rows, columns) def set_current(self, state): """ Set current CD drive :param state: state object with drive info """ if not self.current_cd_drive_name or ( getattr(state, "name", None) and self.current_cd_drive_name != state.name): change_menu = False if not self.current_cd_drive_name: self.current_cd_drive_name = self.cdutil.get_default_cd_drive( )[1] change_menu = True elif state.name != "cd": self.current_cd_drive_name = state.name change_menu = True name = self.current_cd_drive_name tracks = len(self.file_menu.filelist.items) empty = self.cdutil.is_drive_empty(name) if name and empty != 1 and tracks == 0: change_menu = True if change_menu: id = self.cdutil.get_cd_drive_id_by_name( self.current_cd_drive_name) self.current_cd_drive_id = id self.file_menu.selected_index = 0 self.set_file_menu() self.set_screen_title() def set_screen_title(self): """ Set screen title """ title = drive_name = self.current_cd_drive_name try: title = self.util.cd_titles[drive_name] except: pass self.screen_title.set_text(title) def eject_cd(self, state): """ Eject CD :param state: state object with drive info """ if len(str(self.current_cd_drive_id)) == 0: return del self.util.cd_track_names_cache[self.current_cd_drive_id] if state == None or getattr(state, "no_physical_eject", None) == None: self.cdutil.eject_cd(int(self.current_cd_drive_id)) self.util.cd_titles[ self.current_cd_drive_name] = self.current_cd_drive_name self.file_menu.selected_index = 0 self.set_file_menu() self.set_screen_title() def refresh_tracks(self, state): """ Refresh tracks menu :param state: not used """ name = self.current_cd_drive_name if name and self.cdutil.is_drive_empty(name): s = State() s.no_physical_eject = True self.eject_cd(s) self.set_file_menu() self.set_screen_title() def set_file_menu(self): """ Set file menu """ filelist = self.get_filelist() self.file_menu.filelist = filelist self.file_menu.update_buttons() page = filelist.get_current_page() self.file_menu.set_page(filelist.current_item_index_in_page, page) def get_filelist_items(self, get_current_playlist): """ Call player for files in the playlist :return: list of files from playlist """ playlist = get_current_playlist() files = [] if playlist: for n in range(len(playlist)): st = State() st.index = st.comparator_item = n st.file_type = FILE_AUDIO st.file_name = st.url = playlist[n] files.append(st) return files def add_screen_observers(self, update_observer, redraw_observer): """ Add screen observers :param update_observer: observer for updating the screen :param redraw_observer: observer to redraw the whole screen """ Screen.add_screen_observers(self, update_observer, redraw_observer) self.file_menu.add_menu_observers(update_observer, redraw_observer=None, release=False) self.file_menu.add_change_folder_listener(redraw_observer) self.file_menu.add_menu_navigation_listeners(redraw_observer) self.file_menu.add_left_number_listener(redraw_observer) self.file_menu.add_right_number_listener(redraw_observer) self.navigator.add_observers(update_observer, redraw_observer)
def __init__(self, util, connected_cd_drives, bounding_box, listeners, bgr): """ Initializer :param util: utility object :param bounding_box: bounding box :param listeners: buttons listeners :param bgr: menu background """ Container.__init__(self, util) self.factory = Factory(util) self.name = "navigator" self.content = bounding_box self.content_x = bounding_box.x self.content_y = bounding_box.y self.menu_buttons = [] self.config = util.config arrow_layout = BorderLayout(bounding_box) arrow_layout.set_percent_constraints(0, 0, PERCENT_ARROW_WIDTH, PERCENT_ARROW_WIDTH) constr = arrow_layout.LEFT self.left_button = self.factory.create_page_down_button( constr, "0", 40, 100) self.left_button.add_release_listener(listeners[GO_LEFT_PAGE]) self.add_component(self.left_button) self.menu_buttons.append(self.left_button) constr = arrow_layout.RIGHT self.right_button = self.factory.create_page_up_button( constr, "0", 40, 100) self.right_button.add_release_listener(listeners[GO_RIGHT_PAGE]) self.add_component(self.right_button) self.menu_buttons.append(self.right_button) layout = GridLayout(arrow_layout.CENTER) p = self.config[AUDIO][PLAYER_NAME] show_drives = connected_cd_drives > 1 and (p == VLC_NAME or p == MPD_NAME) if show_drives: layout.set_pixel_constraints(1, 5, 1, 0) else: layout.set_pixel_constraints(1, 4, 1, 0) layout.current_constraints = 0 image_size = 56 constr = layout.get_next_constraints() self.home_button = self.factory.create_button( KEY_HOME, KEY_HOME, constr, listeners[KEY_HOME], bgr, image_size_percent=image_size) self.add_component(self.home_button) self.menu_buttons.append(self.home_button) if show_drives: constr = layout.get_next_constraints() self.cd_drives_button = self.factory.create_button( KEY_CD_PLAYERS, KEY_ROOT, constr, listeners[KEY_CD_PLAYERS], bgr, image_size_percent=image_size) self.add_component(self.cd_drives_button) self.menu_buttons.append(self.cd_drives_button) constr = layout.get_next_constraints() self.refresh_button = self.factory.create_button( KEY_REFRESH, KEY_SETUP, constr, listeners[KEY_REFRESH], bgr, image_size_percent=image_size) self.add_component(self.refresh_button) self.menu_buttons.append(self.refresh_button) constr = layout.get_next_constraints() self.eject_button = self.factory.create_button( KEY_EJECT, KEY_PARENT, constr, listeners[KEY_EJECT], bgr, image_size_percent=image_size) self.add_component(self.eject_button) self.menu_buttons.append(self.eject_button) constr = layout.get_next_constraints() self.back_button = self.factory.create_button( KEY_BACK, KEY_BACK, constr, None, bgr, image_size_percent=image_size) self.back_button.add_release_listener(listeners[GO_BACK]) self.back_button.add_release_listener(listeners[KEY_PLAYER]) self.add_component(self.back_button) self.menu_buttons.append(self.back_button)
class Screen(Container): """ Base class for all screens with menu and navigator """ def __init__(self, util, title_key, percent_bottom_height=0, voice_assistant=None, screen_title_name="screen_title", create_dynamic_title=False, title_layout=None, title=None, bgr=None): """ Initializer :param util: utility object :param title_key: the resource bundle key for the screen title """ self.util = util self.factory = Factory(util) self.config = util.config self.bounding_box = util.screen_rect if title_key: self.name = title_key else: self.name = "tmp" bg = self.util.get_background(self.name, bgr) self.bgr_type = bg[0] self.bgr = bg[1] self.bgr_key = bg[5] Container.__init__(self, util, bounding_box=self.bounding_box, background=bg[1], content=bg[2], image_filename=bg[3]) self.original_image_filename = None try: self.original_image_filename = bg[4].replace("\\", "/") except: pass self.layout = BorderLayout(util.screen_rect) self.layout.set_percent_constraints(PERCENT_TOP_HEIGHT, percent_bottom_height, 0, 0) self.voice_assistant = voice_assistant self.font_size = int((self.layout.TOP.h * PERCENT_TITLE_FONT)/100.0) b = self.config[BACKGROUND][HEADER_BGR_COLOR] c = self.config[COLORS][COLOR_CONTRAST] t_layout = self.layout.TOP if title_layout: t_layout = title_layout if create_dynamic_title: self.screen_title = self.factory.create_dynamic_text(screen_title_name, t_layout, b, c, self.font_size) else: self.screen_title = self.factory.create_output_text(screen_title_name, t_layout, b, c, self.font_size) if title: self.screen_title.set_text(title) else: if title_key and len(title_key) > 0: try: label = self.config[LABELS][title_key] self.screen_title.set_text(label) except: pass if voice_assistant: self.screen_title.add_select_listener(self.handle_voice_assistant) self.layout.TOP.w += 1 self.voice_command = self.factory.create_output_text("voice_command", self.layout.TOP, b, c, self.font_size) self.voice_command.add_select_listener(self.handle_voice_assistant) self.voice_assistant.add_text_recognized_listener(self.text_recognized) self.voice_assistant.assistant.add_start_conversation_listener(self.start_conversation) self.voice_assistant.assistant.add_stop_conversation_listener(self.stop_conversation) if voice_assistant and voice_assistant.is_running(): self.title_selected = False else: self.title_selected = True self.draw_title_bar() self.player_screen = False self.update_web_observer = None self.update_web_title = None self.loading_listeners = [] self.LOADING = util.config[LABELS][KEY_LOADING] self.animated_title = False def add_component(self, c): """ Add screen component :param c: component to add """ if c: c.set_parent_screen(self) Container.add_component(self, c) def draw_title_bar(self): """ Draw title bar on top of the screen """ if len(self.components) != 0 and self.title_selected: self.add_component(self.voice_command) self.title_selected = False elif len(self.components) != 0 and not self.title_selected: self.add_component(self.screen_title) self.title_selected = True elif len(self.components) == 0 and self.title_selected: self.add_component(self.screen_title) elif len(self.components) == 0 and not self.title_selected: self.voice_command.set_text(self.config[LABELS][KEY_WAITING_FOR_COMMAND]) self.add_component(self.voice_command) def handle_voice_assistant(self, state=None): """ Start/Stop voice assistant :state: not used """ if self.title_selected: self.voice_assistant.start() else: self.voice_assistant.stop() def text_recognized(self, text): """ Handle text recognized event :text: recognized text """ c = self.config[LABELS][KEY_VA_COMMAND] + " " + text self.voice_command.set_text(c) self.voice_command.clean_draw_update() time.sleep(self.config[VOICE_ASSISTANT][VOICE_COMMAND_DISPLAY_TIME]) if self.update_web_title: self.update_web_title(self.voice_command) def start_conversation(self, event): """ Start voice conversation :event: not used """ if self.visible: self.voice_command.set_visible(True) self.voice_command.set_text_no_draw(self.config[LABELS][KEY_WAITING_FOR_COMMAND]) self.components[0] = self.voice_command self.title_selected = False if self.visible: self.voice_command.clean_draw_update() if self.update_web_observer: self.update_web_observer() def stop_conversation(self): """ Stop voice conversation """ if self.visible: self.screen_title.set_visible(True) self.components[0] = self.screen_title self.title_selected = True if self.visible: self.screen_title.clean_draw_update() if self.update_web_observer: self.update_web_observer() def add_menu(self, menu): """ Add menu to the screen :param menu: the menu to add """ self.menu = menu self.add_component(menu) def add_navigator(self, navigator): """ Add navigator to the screen :param menu: the menu to add """ self.navigator = navigator self.add_component(navigator) def enable_player_screen(self, flag): """ Enable player screen :param flag: enable/disable flag """ pass def exit_screen(self): """ Complete actions required to save screen state """ self.set_visible(False) def add_screen_observers(self, update_observer, redraw_observer, title_to_json=None): """ Add screen observers. :param update_observer: observer for updating the screen :param redraw_observer: observer to redraw the whole screen """ self.update_web_observer = redraw_observer self.update_web_title = title_to_json def set_loading(self, name=None, menu_bb=None, text=None): """ Show Loading... sign :param name: screen title :param menu_bb: menu bounding box :param text: screen text """ b = self.config[BACKGROUND][MENU_BGR_COLOR] f = self.config[COLORS][COLOR_BRIGHT] fs = int(self.bounding_box.h * 0.07) if menu_bb != None: bb = menu_bb else: bb = self.bounding_box bb = Rect(bb.x, bb.y + 1, bb.w, bb.h - 1) t = self.factory.create_output_text(self.LOADING, bb, b, f, fs) if not text: t.set_text_no_draw(self.LOADING) else: t.set_text_no_draw(text) if name != None: self.screen_title.set_text(name) self.set_visible(True) self.add_component(t) if getattr(self, "menu", None) != None: self.menu.buttons = {} self.menu.components = [] self.clean_draw_update() self.notify_loading_listeners() def reset_loading(self): """ Remove Loading label """ del self.components[-1] self.notify_loading_listeners() def add_loading_listener(self, listener): """ Add loading listener :param listener: event listener """ if listener not in self.loading_listeners: self.loading_listeners.append(listener) def notify_loading_listeners(self): """ Notify all loading listeners """ for listener in self.loading_listeners: listener(None) def handle_event(self, event): """ Event handler :param event: event to handle """ if not self.visible: return mouse_events = [pygame.MOUSEBUTTONUP, pygame.MOUSEBUTTONDOWN] if event.type in mouse_events and getattr(self, "menu", None) and getattr(self, "navigator", None): event_component = None for comp in self.menu.components: if getattr(comp, "state", None): bb = comp.state.bounding_box else: bb = comp.bounding_box if bb == None: continue if bb.collidepoint(event.pos): event_component = comp for c in self.menu.components: if hasattr(c, "set_selected") and c != comp: c.set_selected(False) c.clean_draw_update() if event_component: self.navigator.unselect() event_component.handle_event(event) if hasattr(self.menu, "current_slider"): self.menu.current_slider = event_component.id if self.update_web_observer: self.update_web_observer() return for comp in self.navigator.components: if getattr(comp, "state", None): bb = comp.state.bounding_box else: bb = comp.bounding_box if bb == None: continue if bb.collidepoint(event.pos): event_component = comp break if event_component: for c in self.menu.components: if getattr(c, "selected", False): c.set_selected(False) c.clean_draw_update() elif hasattr(c, "slider"): c.slider.set_knob_off() self.navigator.unselect() event_component.handle_event(event) if self.update_web_observer: self.update_web_observer() else: Container.handle_event(self, event) self.update_web_observer() def get_menu_bottom_exit_xy(self, x): """ Get bottom exit coordinates :param x: event x coordinate """ button = None xy = None for b in self.menu.components: if getattr(b, "bounding_box", None) == None: continue y = b.bounding_box.y + 5 if b.bounding_box.collidepoint((x, y)): button = b if button == None: num = len(self.menu.components) if num != 0: button = self.menu.components[num - 1] if button != None and getattr(button, "bounding_box", None) != None: xy = (button.bounding_box.x + (button.bounding_box.w / 2), button.bounding_box.y) return xy def link_borders(self, navigator_top_bottom_exit=True, first_index=None, last_index=None): """ Link components borders for the arrow keys navigation :param navigator_top_bottom_exit: """ if not hasattr(self.navigator, "components"): return margin = 10 num = len(self.navigator.components) first_menu_comp = last_menu_comp = None if getattr(self, "menu", None): self.menu.exit_top_y = self.menu.exit_bottom_y = self.menu.bounding_box.y + self.menu.bounding_box.h + margin first_nav_comp = self.navigator.components[0] last_nav_comp = self.navigator.components[num - 1] self.menu.exit_right_x = first_nav_comp.bounding_box.x + margin self.menu.exit_right_y = first_nav_comp.bounding_box.y + margin self.menu.exit_left_x = last_nav_comp.bounding_box.x + last_nav_comp.bounding_box.w - margin self.menu.exit_left_y = last_nav_comp.bounding_box.y + margin if first_index == None: first_index = 0 if last_index == None: last_index = len(self.menu.components) - 1 if len(self.menu.components) > 0: first_menu_comp = self.menu.components[first_index] last_menu_comp = self.menu.components[last_index] if getattr(self, "menu", None) and len(self.menu.components) == 0: navigator_top_bottom_exit = False for index, comp in enumerate(self.navigator.components): if navigator_top_bottom_exit and getattr(self, "menu", None): xy = self.get_menu_bottom_exit_xy(comp.bounding_box.x + 5) if xy: comp.exit_top_y = xy[1] + margin comp.exit_bottom_y = self.menu.bounding_box.y + margin comp.exit_x = xy[0] else: comp.exit_top_y = None comp.exit_bottom_y = None comp.exit_x = None if index == 0: if last_menu_comp != None: comp.exit_left_x = last_menu_comp.bounding_box.x + margin comp.exit_left_y = last_menu_comp.bounding_box.y + margin else: comp.exit_left_x = self.navigator.components[num - 1].bounding_box.x + margin comp.exit_left_y = self.navigator.components[num - 1].bounding_box.y + margin c = self.navigator.components[index + 1] comp.exit_right_x = c.bounding_box.x + margin elif index == (num - 1): c = self.navigator.components[num - 2] comp.exit_left_x = c.bounding_box.x + margin if first_menu_comp != None: comp.exit_right_x = first_menu_comp.bounding_box.x + margin comp.exit_right_y = first_menu_comp.bounding_box.y + margin else: comp.exit_right_x = self.navigator.components[0].bounding_box.x + margin comp.exit_right_y = self.navigator.components[0].bounding_box.y + margin else: c = self.navigator.components[index - 1] comp.exit_left_x = c.bounding_box.x + margin c = self.navigator.components[index + 1] comp.exit_right_x = c.bounding_box.x + margin
class CollectionBrowserNavigator(Container): """ Topic detail navigator """ def __init__(self, util, bounding_box, listeners): """ Initializer :param util: utility object :param bounding_box: bounding box :param listeners: buttons listeners """ Container.__init__(self, util) self.factory = Factory(util) self.name = "collection.navigator" self.content = bounding_box self.content_x = bounding_box.x self.content_y = bounding_box.y self.listeners = listeners self.menu_buttons = [] self.config = util.config self.use_web = self.config[USAGE][USE_WEB] self.arrow_layout = BorderLayout(bounding_box) self.arrow_layout.set_percent_constraints(0, 0, PERCENT_ARROW_WIDTH, PERCENT_ARROW_WIDTH) self.update_observer = None self.redraw_observer = None self.menu_buttons = [] constr = self.arrow_layout.LEFT self.left_button = self.factory.create_page_down_button( constr, "0", 40, 100) self.left_button.add_release_listener(self.listeners[GO_LEFT_PAGE]) self.add_component(self.left_button) self.menu_buttons.append(self.left_button) constr = self.arrow_layout.RIGHT self.right_button = self.factory.create_page_up_button( constr, "0", 40, 100) self.right_button.add_release_listener(self.listeners[GO_RIGHT_PAGE]) self.add_component(self.right_button) self.menu_buttons.append(self.right_button) layout = GridLayout(self.arrow_layout.CENTER) layout.set_pixel_constraints(1, 5, 1, 0) layout.current_constraints = 0 bgr = util.config[BACKGROUND][FOOTER_BGR_COLOR] self.add_button(KEY_HOME, KEY_HOME, layout, self.listeners[KEY_HOME], bgr) self.add_button(COLLECTION, KEY_PARENT, layout, self.listeners[COLLECTION], bgr) self.add_button(KEY_LIST, KEY_MENU, layout, self.listeners[COLLECTION_TOPIC], bgr) self.add_button(KEY_DETAIL, KEY_SETUP, layout, self.listeners[TOPIC_DETAIL], bgr) self.add_button(KEY_PLAYER, KEY_PLAY_PAUSE, layout, self.listeners[KEY_PLAYER], bgr) if self.use_web and self.update_observer != None: self.add_observers(self.update_observer, self.redraw_observer) def add_button(self, img_name, key, layout, listener, bgr): """ Add button to the navigator :param img_name: button image name :param key: keyboard key :param layout: button layout :param listener: button listener """ c = layout.get_next_constraints() b = self.factory.create_button(img_name, key, c, listener, bgr, source=KEY_NAVIGATOR, image_size_percent=IMAGE_SIZE_PERCENT) self.add_component(b) self.menu_buttons.append(b) def add_observers(self, update_observer, redraw_observer): """ Add screen observers :param update_observer: observer for updating the screen :param redraw_observer: observer to redraw the whole screen """ if self.update_observer == None: self.update_observer = update_observer self.redraw_observer = redraw_observer for b in self.menu_buttons: self.add_button_observers(b, update_observer, redraw_observer)
def __init__(self, util, bounding_box, listeners, bgr, language_url=None): """ Initializer :param util: utility object :param bounding_box: bounding box :param listeners: buttons listeners :param bgr: menu background :param language_url: language constant """ Container.__init__(self, util) self.factory = Factory(util) self.name = "navigator" arrow_layout = BorderLayout(bounding_box) arrow_layout.set_percent_constraints(0, 0, PERCENT_ARROW_WIDTH, PERCENT_ARROW_WIDTH) self.menu_buttons = [] constr = arrow_layout.LEFT self.left_button = self.factory.create_page_down_button( constr, "0", 34, 100) self.left_button.add_release_listener(listeners[GO_LEFT_PAGE]) self.add_component(self.left_button) self.menu_buttons.append(self.left_button) constr = arrow_layout.RIGHT self.right_button = self.factory.create_page_up_button( constr, "0", 34, 100) self.right_button.add_release_listener(listeners[GO_RIGHT_PAGE]) self.add_component(self.right_button) self.menu_buttons.append(self.right_button) layout = GridLayout(arrow_layout.CENTER) if language_url == "": # English-USA buttons = 5 elif language_url == None: #Russian buttons = 6 else: buttons = 4 layout.set_pixel_constraints(1, buttons, 1, 0) layout.current_constraints = 0 image_size = 56 constr = layout.get_next_constraints() self.home_button = self.factory.create_button( KEY_HOME, KEY_HOME, constr, listeners[KEY_HOME], bgr, image_size_percent=image_size) self.add_component(self.home_button) self.menu_buttons.append(self.home_button) if language_url == None: constr = layout.get_next_constraints() self.abc_button = self.factory.create_button( IMAGE_ABC, KEY_SETUP, constr, listeners[GO_USER_HOME], bgr, image_size_percent=image_size) self.add_component(self.abc_button) self.menu_buttons.append(self.abc_button) constr = layout.get_next_constraints() self.new_books_button = self.factory.create_button( IMAGE_NEW_BOOKS, KEY_MENU, constr, listeners[GO_ROOT], bgr, image_size_percent=image_size) self.add_component(self.new_books_button) self.menu_buttons.append(self.new_books_button) if language_url == None or language_url == "": # English-USA or Russian constr = layout.get_next_constraints() self.genre_button = self.factory.create_button( IMAGE_BOOK_GENRE, KEY_ROOT, constr, listeners[GO_TO_PARENT], bgr, image_size_percent=image_size) self.add_component(self.genre_button) self.menu_buttons.append(self.genre_button) constr = layout.get_next_constraints() self.player_button = self.factory.create_button( IMAGE_PLAYER, KEY_PLAY_PAUSE, constr, listeners[GO_PLAYER], bgr, source=BOOK_NAVIGATOR, image_size_percent=image_size) self.add_component(self.player_button) self.menu_buttons.append(self.player_button) constr = layout.get_next_constraints() self.back_button = self.factory.create_button( KEY_BACK, KEY_BACK, constr, None, bgr, source=BOOK_NAVIGATOR_BACK, image_size_percent=image_size) self.back_button.add_release_listener(listeners[GO_BACK]) try: self.back_button.add_release_listener(listeners[KEY_PLAY_FILE]) except: pass self.add_component(self.back_button) self.menu_buttons.append(self.back_button)
def __init__(self, util, listener): """ Initializer :param util: utility object :Param listener: screen listener """ self.name = KEY_INFO self.util = util self.config = util.config self.factory = Factory(util) self.listener = listener self.bg = self.util.get_background(self.name) self.bgr_type = self.bg[0] self.bgr = self.bg[1] self.bgr_key = self.bg[5] self.screen_w = self.config[SCREEN_INFO][WIDTH] self.screen_h = self.config[SCREEN_INFO][HEIGHT] self.lines = 14 font_vertical_percent = 6 self.bounding_box = util.screen_rect Container.__init__(self, util, self.bounding_box, background=self.bg[1], content=self.bg[2], image_filename=self.bg[3]) font_size = int((font_vertical_percent * self.bounding_box.h) / 100) self.f = util.get_font(font_size) center_layout = BorderLayout(self.bounding_box) center_layout.set_percent_constraints(0, 0, 44, 0) left_layout = center_layout.LEFT right_layout = center_layout.CENTER label_layout = GridLayout(left_layout) label_layout.set_pixel_constraints(self.lines, 1) label_layout.get_next_constraints() bb1 = label_layout.get_next_constraints() value_layout = GridLayout(right_layout) value_layout.set_pixel_constraints(self.lines, 1) value_layout.get_next_constraints() bb2 = value_layout.get_next_constraints() title_bb = pygame.Rect(bb1.x, bb1.y, bb1.w + bb2.w, bb1.h) self.title = self.add_title(title_bb) label_layout.get_next_constraints() self.add_label(label_layout, 1, self.config[LABELS]["file.size"] + ":") self.add_label(label_layout, 2, self.config[LABELS]["sample.rate"] + ":") self.add_label(label_layout, 3, self.config[LABELS]["channels"] + ":") self.add_label(label_layout, 4, self.config[LABELS]["bits.per.sample"] + ":") self.add_label(label_layout, 5, self.config[LABELS]["bit.rate"] + ":") label_layout.get_next_constraints() self.add_label(label_layout, 6, self.config[LABELS][GENRE] + ":") self.add_label(label_layout, 7, self.config[LABELS][ARTIST] + ":") self.add_label(label_layout, 8, self.config[LABELS][ALBUM] + ":") self.add_label(label_layout, 9, self.config[LABELS][DATE] + ":") value_layout.get_next_constraints() self.filesize = self.add_value(value_layout, 1) self.sample_rate = self.add_value(value_layout, 2) self.channels = self.add_value(value_layout, 3) self.bits = self.add_value(value_layout, 4) self.bit_rate = self.add_value(value_layout, 5) value_layout.get_next_constraints() self.genre = self.add_value(value_layout, 6) self.artist = self.add_value(value_layout, 7) self.album = self.add_value(value_layout, 8) self.date = self.add_value(value_layout, 9) self.values = [ self.filesize, self.sample_rate, self.channels, self.bits, self.bit_rate, self.genre, self.artist, self.album, self.date ]
def __init__(self, util, bounding_box, listeners, bgr, pages): """ Initializer :param util: utility object :param bounding_box: bounding box :param listeners: buttons listeners :param bgr: menu background """ Container.__init__(self, util) self.factory = Factory(util) self.name = "navigator" self.content = bounding_box self.content_x = bounding_box.x self.content_y = bounding_box.y self.menu_buttons = [] arrow_layout = BorderLayout(bounding_box) arrow_layout.set_percent_constraints(0, 0, PERCENT_ARROW_WIDTH, PERCENT_ARROW_WIDTH) if pages > 1: constr = arrow_layout.LEFT self.left_button = self.factory.create_page_down_button( constr, "0", 40, 100) self.left_button.add_release_listener(listeners[GO_LEFT_PAGE]) self.add_component(self.left_button) self.menu_buttons.append(self.left_button) constr = arrow_layout.RIGHT self.right_button = self.factory.create_page_up_button( constr, "0", 40, 100) self.right_button.add_release_listener(listeners[GO_RIGHT_PAGE]) self.add_component(self.right_button) self.menu_buttons.append(self.right_button) if pages > 1: layout = GridLayout(arrow_layout.CENTER) else: layout = GridLayout(bounding_box) layout.set_pixel_constraints(1, 2, 1, 0) layout.current_constraints = 0 image_size = 64 constr = layout.get_next_constraints() self.home_button = self.factory.create_button( KEY_HOME, KEY_HOME, constr, listeners[KEY_HOME], bgr, image_size_percent=image_size) self.add_component(self.home_button) self.menu_buttons.append(self.home_button) constr = layout.get_next_constraints() self.player_button = self.factory.create_button( KEY_PLAYER, KEY_PLAY_PAUSE, constr, listeners[KEY_PLAYER], bgr, image_size_percent=image_size) self.add_component(self.player_button) self.menu_buttons.append(self.player_button)
class TimerScreen(Screen): """ Timer Screen """ def __init__(self, util, listeners, voice_assistant, timer_lock, start_timer_thread): """ Initializer :param util: utility object :param listeners: screen navigator listeners :param voice_assistant: voice assistant :param timer_lock: lock object :param start_timer_thread: start timer thread function """ self.util = util self.timer_lock = timer_lock self.start_timer_thread = start_timer_thread self.config = util.config self.screen_layout = BorderLayout(util.screen_rect) self.screen_layout.set_percent_constraints(PERCENT_TITLE, PERCENT_NAV_HEIGHT, 0, 0) Screen.__init__(self, util, "", PERCENT_NAV_HEIGHT, voice_assistant, "timer_title", title_layout=self.screen_layout.TOP) self.bounding_box = util.screen_rect label = self.config[LABELS][TIMER] self.screen_title.set_text(label) try: self.config[TIMER] except: self.config[TIMER] = {} c = GridLayout(self.screen_layout.CENTER) c.set_pixel_constraints(2, 1) layout = BorderLayout(c.get_next_constraints()) layout.set_percent_constraints(0, 0, PERCENT_CLOCK, 100 - PERCENT_CLOCK) gap = layout.h * 0.1 layout.LEFT.w -= gap * 3 layout.LEFT.h -= gap digits = util.image_util.get_flipclock_digits(layout.LEFT) change_codes = [ HOURS_INCREMENT_SLEEP, MINUTES_INCREMENT_SLEEP, HOURS_DECREMENT_SLEEP, MINUTES_DECREMENT_SLEEP ] self.sleep_menu = SleepMenu(util, layout, gap, digits, self.handle_button, timer_lock, self.sleep_change_callback, change_codes) self.active_key_menu = self.sleep_menu self.add_component(self.sleep_menu) self.menu_index = -1 self.sleep_menu.clock.add_change_listener(self.handle_clock_change) layout = BorderLayout(c.get_next_constraints()) layout.set_percent_constraints(0, 0, PERCENT_CLOCK, 100 - PERCENT_CLOCK) change_codes = [ HOURS_INCREMENT_WAKE_UP, MINUTES_INCREMENT_WAKE_UP, HOURS_DECREMENT_WAKE_UP, MINUTES_DECREMENT_WAKE_UP ] self.wake_up_menu = WakeUpMenu(util, layout, gap, digits, self.handle_button, timer_lock, self.wake_up_change_callback, change_codes) self.add_component(self.wake_up_menu) self.wake_up_menu.clock.add_change_listener(self.handle_clock_change) self.navigator = TimerNavigator(self.util, self.screen_layout.BOTTOM, listeners) self.navigator.components[0].set_selected(True) self.add_component(self.navigator) self.home_button = self.navigator.get_button_by_name(KEY_HOME) self.sleep_now_button = self.navigator.get_button_by_name(SLEEP_NOW) self.player_button = self.navigator.get_button_by_name(KEY_PLAYER) self.menu_functions = { HOURS_INCREMENT_SLEEP: self.sleep_menu.clock.increment_hours, MINUTES_INCREMENT_SLEEP: self.sleep_menu.clock.increment_minutes, HOURS_DECREMENT_SLEEP: self.sleep_menu.clock.decrement_hours, MINUTES_DECREMENT_SLEEP: self.sleep_menu.clock.decrement_minutes, HOURS_INCREMENT_WAKE_UP: self.wake_up_menu.clock.increment_hours, MINUTES_INCREMENT_WAKE_UP: self.wake_up_menu.clock.increment_minutes, HOURS_DECREMENT_WAKE_UP: self.wake_up_menu.clock.decrement_hours, MINUTES_DECREMENT_WAKE_UP: self.wake_up_menu.clock.decrement_minutes } self.current_button = None self.link_borders() self.clean_draw_update() def link_borders(self): """ Link components borders for the arrow keys navigation """ margin = 10 self.enter_naviagtor_x = self.navigator.components[ 0].bounding_box.x + margin self.enter_naviagtor_y = self.navigator.components[ 0].bounding_box.y + margin self.exit_naviagtor_x = self.navigator.components[ 2].bounding_box.x + margin self.exit_naviagtor_y = self.navigator.components[ 2].bounding_box.y + margin self.enter_poweroff_x = self.sleep_menu.poweroff_button.bounding_box.x + margin self.enter_poweroff_y = self.sleep_menu.poweroff_button.bounding_box.y + margin self.enter_sleep_x = self.sleep_menu.sleep_button.bounding_box.x + margin self.enter_sleep_y = self.sleep_menu.sleep_button.bounding_box.y + margin self.enter_wake_up_x = self.wake_up_menu.button.bounding_box.x + margin self.enter_wake_up_y = self.wake_up_menu.button.bounding_box.y + margin self.exit_sleep_x = self.sleep_menu.bounding_box.x + self.sleep_menu.bounding_box.w + margin self.exit_sleep_incr_y = self.sleep_menu.bounding_box.y + margin self.exit_sleep_decr_y = self.sleep_menu.bounding_box.y + self.sleep_menu.bounding_box.h - margin self.exit_wake_up_x = self.wake_up_menu.bounding_box.x + self.wake_up_menu.bounding_box.w + margin self.exit_wake_up_y = self.wake_up_menu.bounding_box.y + margin self.exit_sleep_top_hour_x = self.exit_wake_up_bottom_hour_x = self.sleep_menu.clock.h1.bounding_box.x + self.sleep_menu.clock.h1.bounding_box.w self.exit_sleep_top_min_x = self.exit_wake_up_bottom_min_x = self.sleep_menu.clock.m1.bounding_box.x + self.sleep_menu.clock.m1.bounding_box.w self.exit_wake_up_bottom_y = self.wake_up_menu.bounding_box.y + self.wake_up_menu.bounding_box.h + margin self.sleep_menu.sleep_button.exit_top_y = self.screen_layout.BOTTOM.y + margin self.sleep_menu.sleep_button.exit_bottom_y = self.sleep_menu.poweroff_button.bounding_box.y + margin self.sleep_menu.sleep_button.exit_left_x = self.sleep_menu.clock.m2.top.x + self.sleep_menu.clock.m2.top.w - margin self.sleep_menu.sleep_button.exit_right_x = self.sleep_menu.clock.h1.top.x + margin self.sleep_menu.sleep_button.exit_right_y = self.sleep_menu.clock.h1.bottom.y + margin self.sleep_menu.poweroff_button.exit_top_y = self.sleep_menu.sleep_button.bounding_box.y + self.sleep_menu.sleep_button.bounding_box.h - margin self.sleep_menu.poweroff_button.exit_bottom_y = self.wake_up_menu.button.bounding_box.y + margin self.sleep_menu.poweroff_button.exit_left_x = self.sleep_menu.clock.m2.bottom.x + self.sleep_menu.clock.m2.bottom.w - margin self.sleep_menu.poweroff_button.exit_right_x = self.sleep_menu.clock.h1.bottom.x + margin self.sleep_menu.poweroff_button.exit_right_y = self.wake_up_menu.clock.h1.top.y + margin self.wake_up_menu.button.exit_top_y = self.sleep_menu.poweroff_button.bounding_box.y + self.sleep_menu.poweroff_button.bounding_box.h - margin self.wake_up_menu.button.exit_bottom_y = self.screen_layout.BOTTOM.y + margin self.wake_up_menu.button.exit_left_x = self.wake_up_menu.clock.m2.top.x + margin self.wake_up_menu.button.exit_left_y = self.wake_up_menu.clock.m2.top.y + margin self.wake_up_menu.button.exit_right_x = self.wake_up_menu.clock.h1.bottom.x + margin self.wake_up_menu.button.exit_right_y = self.wake_up_menu.clock.h1.bottom.y + margin for index, comp in enumerate(self.navigator.components): comp.exit_top_y = self.wake_up_menu.clock.h1.bounding_box.y + self.wake_up_menu.clock.h1.bounding_box.h - margin comp.exit_bottom_y = self.sleep_menu.clock.h1.bounding_box.y + margin if index == 0: comp.exit_left_x = self.wake_up_menu.clock.m2.bottom.x + margin comp.exit_left_y = self.wake_up_menu.clock.m2.bottom.y + margin comp.exit_right_x = self.sleep_now_button.bounding_box.x + margin elif index == 1: comp.exit_left_x = self.home_button.bounding_box.x + margin comp.exit_right_x = self.player_button.bounding_box.x + margin elif index == 2: comp.exit_left_x = self.sleep_now_button.bounding_box.x + margin comp.exit_right_x = self.sleep_menu.clock.h1.top.x + margin comp.exit_right_y = self.sleep_menu.clock.h1.top.y + margin def set_parent_screen(self, scr): """ Add parent screen :param scr: parent screen """ self.sleep_menu.set_parent_screen(scr) self.wake_up_menu.set_parent_screen(scr) self.navigator.set_parent_screen(scr) def sleep_change_callback(self): """ Callback for sleep menu """ if self.active_key_menu != self.sleep_menu: self.wake_up_menu.clock.reset_key() self.active_key_menu = self.sleep_menu self.unselect_sleep_buttons() def wake_up_change_callback(self): """ Callback for wake up menu """ if self.active_key_menu != self.wake_up_menu: self.sleep_menu.clock.reset_key() self.active_key_menu = self.wake_up_menu self.unselect_wake_up() def handle_event(self, event): """ Handle screen event :param event: the event to handle """ if not self.visible or event.type == pygame.MOUSEMOTION: return mouse_events = [pygame.MOUSEBUTTONUP, pygame.MOUSEBUTTONDOWN] if event.type in mouse_events: self.handle_mouse_event(event) return if event.type == SELECT_EVENT_TYPE: self.handle_select_action(event) return if not (event.type == USER_EVENT_TYPE and event.sub_type == SUB_TYPE_KEYBOARD and event.action == pygame.KEYUP): Container.handle_event(self, event) return keys = [ kbd_keys[KEY_SELECT], kbd_keys[KEY_LEFT], kbd_keys[KEY_RIGHT], kbd_keys[KEY_UP], kbd_keys[KEY_DOWN] ] if event.keyboard_key in keys and self.menu_index == -1: if self.current_button == self.sleep_menu.sleep_button: self.sleep_menu.sleep_button.handle_event(event) if self.config[TIMER][SLEEP]: self.sleep_menu.sleep_button.set_selected(True) self.sleep_menu.sleep_button.clean_draw_update() else: if event.keyboard_key == kbd_keys[KEY_SELECT]: self.select_home() elif self.current_button == self.sleep_menu.poweroff_button: self.sleep_menu.poweroff_button.handle_event(event) if self.config[TIMER][POWEROFF]: self.sleep_menu.poweroff_button.set_selected(True) self.sleep_menu.poweroff_button.clean_draw_update() else: if event.keyboard_key == kbd_keys[KEY_SELECT]: self.select_home() elif self.current_button == self.wake_up_menu.button: self.wake_up_menu.button.handle_event(event) if self.config[TIMER][WAKE_UP]: self.wake_up_menu.button.set_selected(True) self.wake_up_menu.button.clean_draw_update() else: if event.keyboard_key == kbd_keys[KEY_SELECT]: self.select_home() else: self.navigator.handle_event(event) return if event.keyboard_key == kbd_keys[KEY_SELECT]: self.menu_functions[self.menu_index]() elif event.keyboard_key == kbd_keys[KEY_RIGHT]: if self.menu_index == MINUTES_INCREMENT_WAKE_UP: self.wake_up_menu.clock.reset_key() self.menu_index = -1 self.util.post_exit_event(self.enter_wake_up_x, self.enter_wake_up_y, self.wake_up_menu) elif self.menu_index == MINUTES_DECREMENT_WAKE_UP: self.wake_up_menu.clock.reset_key() self.menu_index = -1 self.util.post_exit_event(self.enter_naviagtor_x, self.enter_naviagtor_y, self.wake_up_menu) elif self.menu_index == MINUTES_INCREMENT_SLEEP: self.sleep_menu.clock.reset_key() self.menu_index = -1 self.util.post_exit_event(self.exit_sleep_x, self.exit_sleep_incr_y, self.sleep_menu) elif self.menu_index == MINUTES_DECREMENT_SLEEP: self.sleep_menu.clock.reset_key() self.menu_index = -1 self.util.post_exit_event(self.exit_sleep_x, self.exit_sleep_decr_y, self.sleep_menu) else: self.menu_index += 1 self.wake_up_menu.clock.set_key_position(self.menu_index) self.sleep_menu.clock.set_key_position(self.menu_index) elif event.keyboard_key == kbd_keys[KEY_LEFT]: if self.menu_index == HOURS_INCREMENT_WAKE_UP: self.wake_up_menu.clock.reset_key() self.menu_index = -1 self.util.post_exit_event(self.enter_poweroff_x, self.enter_poweroff_y, self.wake_up_menu) elif self.menu_index == HOURS_DECREMENT_WAKE_UP: self.wake_up_menu.clock.reset_key() self.menu_index = -1 self.util.post_exit_event(self.exit_wake_up_x, self.exit_wake_up_y, self.wake_up_menu) elif self.menu_index == HOURS_INCREMENT_SLEEP: self.sleep_menu.clock.reset_key() self.menu_index = -1 self.util.post_exit_event(self.exit_naviagtor_x, self.exit_naviagtor_y, self.sleep_menu) elif self.menu_index == HOURS_DECREMENT_SLEEP: self.sleep_menu.clock.reset_key() self.menu_index = -1 self.util.post_exit_event(self.enter_sleep_x, self.enter_sleep_y, self.sleep_menu) else: self.menu_index -= 1 self.wake_up_menu.clock.set_key_position(self.menu_index) self.sleep_menu.clock.set_key_position(self.menu_index) elif event.keyboard_key == kbd_keys[KEY_UP]: if self.menu_index == HOURS_INCREMENT_SLEEP: self.sleep_menu.clock.reset_key() self.menu_index = -1 self.util.post_exit_event(self.exit_sleep_top_hour_x, self.exit_wake_up_bottom_y, self.sleep_menu) elif self.menu_index == MINUTES_INCREMENT_SLEEP: self.sleep_menu.clock.reset_key() self.menu_index = -1 self.util.post_exit_event(self.exit_sleep_top_min_x, self.exit_wake_up_bottom_y, self.sleep_menu) elif self.menu_index == HOURS_DECREMENT_SLEEP: self.handle_up_down(HOURS_INCREMENT_SLEEP, self.sleep_menu) elif self.menu_index == MINUTES_DECREMENT_SLEEP: self.handle_up_down(MINUTES_INCREMENT_SLEEP, self.sleep_menu) elif self.menu_index == HOURS_INCREMENT_WAKE_UP: self.handle_up_down(HOURS_DECREMENT_SLEEP, self.sleep_menu, self.wake_up_menu, self.sleep_menu) elif self.menu_index == MINUTES_INCREMENT_WAKE_UP: self.handle_up_down(MINUTES_DECREMENT_SLEEP, self.sleep_menu, self.wake_up_menu, self.sleep_menu) elif self.menu_index == HOURS_DECREMENT_WAKE_UP: self.handle_up_down(HOURS_INCREMENT_WAKE_UP, self.wake_up_menu) elif self.menu_index == MINUTES_DECREMENT_WAKE_UP: self.handle_up_down(MINUTES_INCREMENT_WAKE_UP, self.wake_up_menu) elif event.keyboard_key == kbd_keys[KEY_DOWN]: if self.menu_index == HOURS_INCREMENT_SLEEP: self.handle_up_down(HOURS_DECREMENT_SLEEP, self.sleep_menu) elif self.menu_index == MINUTES_INCREMENT_SLEEP: self.handle_up_down(MINUTES_DECREMENT_SLEEP, self.sleep_menu) elif self.menu_index == HOURS_DECREMENT_SLEEP: self.handle_up_down(HOURS_INCREMENT_WAKE_UP, self.wake_up_menu, self.sleep_menu, self.wake_up_menu) elif self.menu_index == MINUTES_DECREMENT_SLEEP: self.handle_up_down(MINUTES_INCREMENT_WAKE_UP, self.wake_up_menu, self.sleep_menu, self.wake_up_menu) elif self.menu_index == HOURS_INCREMENT_WAKE_UP: self.handle_up_down(HOURS_DECREMENT_WAKE_UP, self.wake_up_menu) elif self.menu_index == MINUTES_INCREMENT_WAKE_UP: self.handle_up_down(MINUTES_DECREMENT_WAKE_UP, self.wake_up_menu) elif self.menu_index == HOURS_DECREMENT_WAKE_UP: self.wake_up_menu.clock.reset_key() self.menu_index = -1 self.util.post_exit_event(self.exit_wake_up_bottom_hour_x, self.exit_wake_up_bottom_y, self.wake_up_menu) elif self.menu_index == MINUTES_DECREMENT_WAKE_UP: self.wake_up_menu.clock.reset_key() self.menu_index = -1 self.util.post_exit_event(self.exit_wake_up_bottom_min_x, self.exit_wake_up_bottom_y, self.wake_up_menu) else: Container.handle_event(self, event) if event.keyboard_key in keys and self.update_web_observer != None: self.update_web_observer() def select_home(self): """ Select home button """ self.current_button = None self.home_button.set_selected(True) self.home_button.clean_draw_update() def handle_select_action(self, event): """ Handle select action :param event: the event """ pos = (event.x - 3, event.y) if self.sleep_menu.clock.h1.top.collidepoint( pos) or self.sleep_menu.clock.h2.top.collidepoint(pos): self.handle_up_down(HOURS_INCREMENT_SLEEP, self.sleep_menu) elif self.sleep_menu.clock.m1.top.collidepoint( pos) or self.sleep_menu.clock.m2.top.collidepoint(pos): self.handle_up_down(MINUTES_INCREMENT_SLEEP, self.sleep_menu) elif self.sleep_menu.clock.h1.bottom.collidepoint( pos) or self.sleep_menu.clock.h2.bottom.collidepoint(pos): self.handle_up_down(HOURS_DECREMENT_SLEEP, self.sleep_menu) elif self.sleep_menu.clock.m1.bottom.collidepoint( pos) or self.sleep_menu.clock.m2.bottom.collidepoint(pos): self.handle_up_down(MINUTES_DECREMENT_SLEEP, self.sleep_menu) elif self.wake_up_menu.clock.h1.top.collidepoint( pos) or self.wake_up_menu.clock.h2.top.collidepoint(pos): self.handle_up_down(HOURS_INCREMENT_WAKE_UP, self.wake_up_menu) elif self.wake_up_menu.clock.m1.top.collidepoint( pos) or self.wake_up_menu.clock.m2.top.collidepoint(pos): self.handle_up_down(MINUTES_INCREMENT_WAKE_UP, self.wake_up_menu) elif self.wake_up_menu.clock.h1.bottom.collidepoint( pos) or self.wake_up_menu.clock.h2.bottom.collidepoint(pos): self.handle_up_down(HOURS_DECREMENT_WAKE_UP, self.wake_up_menu) elif self.wake_up_menu.clock.m1.bottom.collidepoint( pos) or self.wake_up_menu.clock.m2.bottom.collidepoint(pos): self.handle_up_down(MINUTES_DECREMENT_WAKE_UP, self.wake_up_menu) elif self.screen_layout.BOTTOM.collidepoint(pos): self.navigator.handle_event(event) for c in self.navigator.components: if c.selected: self.current_button = c elif self.sleep_menu.sleep_button.bounding_box.collidepoint(pos): self.sleep_menu.sleep_button.handle_event(event) self.current_button = self.sleep_menu.sleep_button elif self.sleep_menu.poweroff_button.bounding_box.collidepoint(pos): self.sleep_menu.poweroff_button.handle_event(event) self.current_button = self.sleep_menu.poweroff_button elif self.wake_up_menu.button.bounding_box.collidepoint(pos): self.wake_up_menu.button.handle_event(event) self.current_button = self.wake_up_menu.button self.update_web_observer() def handle_mouse_event(self, event): """ Handle mouse event :param event: the event """ event_component = None for comp in self.sleep_menu.components: bb = comp.bounding_box if bb.collidepoint(event.pos): event_component = comp if comp == self.sleep_menu.sleep_button or comp == self.sleep_menu.poweroff_button: if comp == self.sleep_menu.sleep_button: self.current_button = self.sleep_menu.sleep_button elif comp == self.sleep_menu.poweroff_button: self.current_button = self.sleep_menu.poweroff_button self.sleep_menu.clock.reset_key() self.wake_up_menu.clock.reset_key() break for comp in self.wake_up_menu.components: bb = comp.bounding_box if bb.collidepoint(event.pos): event_component = comp if comp == self.wake_up_menu.button: self.current_button = self.wake_up_menu.button self.sleep_menu.clock.reset_key() self.wake_up_menu.clock.reset_key() self.menu_index = -1 break if event_component: self.navigator.unselect() event_component.handle_event(event) return for comp in self.navigator.components: if getattr(comp, "state", None): bb = comp.state.bounding_box else: bb = comp.bounding_box if bb == None: continue if bb.collidepoint(event.pos): event_component = comp break if event_component: self.sleep_menu.clock.reset_key() self.wake_up_menu.clock.reset_key() self.menu_index = -1 self.navigator.unselect() event_component.handle_event(event) def handle_up_down(self, position, set_menu, reset_menu=None, active_menu=None): """ Handle up and down keys :param position: key position :param set_menu: menu to set key position :param reset_menu: menu to reset :param active_menu: active menu """ if reset_menu != None: reset_menu.clock.reset_key() if active_menu != None: self.active_key_menu = active_menu set_menu.clock.set_key_position(position) self.menu_index = position def handle_clock_change(self, change_code): """ Clock change handler :param change_code: change code """ if change_code == HOURS_INCREMENT_SLEEP: self.menu_index = HOURS_INCREMENT_SLEEP elif change_code == MINUTES_INCREMENT_SLEEP: self.menu_index = MINUTES_INCREMENT_SLEEP elif change_code == HOURS_DECREMENT_SLEEP: self.menu_index = HOURS_DECREMENT_SLEEP elif change_code == MINUTES_DECREMENT_SLEEP: self.menu_index = MINUTES_DECREMENT_SLEEP elif change_code == HOURS_INCREMENT_WAKE_UP: self.menu_index = HOURS_INCREMENT_WAKE_UP elif change_code == MINUTES_INCREMENT_WAKE_UP: self.menu_index = MINUTES_INCREMENT_WAKE_UP elif change_code == HOURS_DECREMENT_WAKE_UP: self.menu_index = HOURS_DECREMENT_WAKE_UP elif change_code == MINUTES_DECREMENT_WAKE_UP: self.menu_index = MINUTES_DECREMENT_WAKE_UP def handle_button(self, state): """ Button event handler :param state: button state """ button = state.event_origin name = button.state.name if name == SLEEP: self.handle_sleep_button(button) elif name == POWEROFF: self.handle_poweroff_button(button) elif name == WAKE_UP: self.handle_wake_up_button(button) def handle_sleep_button(self, button): """ Sleep button handler :button: sleep button """ if self.sleep_menu.sleep_selected: button.set_selected(False) with self.timer_lock: self.config[TIMER][SLEEP] = False self.sleep_menu.sleep_selected = False if self.wake_up_menu.button_selected: self.unselect_wake_up() else: button.set_selected(True) with self.timer_lock: self.config[TIMER][SLEEP] = True self.config[TIMER][POWEROFF] = False self.sleep_menu.sleep_selected = True self.sleep_menu.poweroff_selected = False self.sleep_menu.poweroff_button.set_selected(False) self.sleep_menu.poweroff_button.clean_draw_update() self.start_timer_thread() button.clean_draw_update() def handle_poweroff_button(self, button): """ Poweroff button handler :button: poweroff button """ if self.sleep_menu.poweroff_selected: button.set_selected(False) with self.timer_lock: self.config[TIMER][POWEROFF] = False self.sleep_menu.poweroff_selected = False else: button.set_selected(True) with self.timer_lock: self.config[TIMER][POWEROFF] = True self.config[TIMER][SLEEP] = False self.sleep_menu.poweroff_selected = True self.sleep_menu.sleep_selected = False self.sleep_menu.sleep_button.set_selected(False) self.sleep_menu.sleep_button.clean_draw_update() if self.wake_up_menu.button_selected: self.unselect_wake_up() self.start_timer_thread() button.clean_draw_update() def handle_wake_up_button(self, button): """ Wake up button handler :button: wake up button """ if self.wake_up_menu.button_selected: self.unselect_wake_up() else: button.set_selected(True) with self.timer_lock: self.config[TIMER][WAKE_UP] = True self.wake_up_menu.button_selected = True self.wake_up_menu.button.clean_draw_update() def unselect_sleep_buttons(self): """ Unselect sleep and poweroff buttons """ with self.timer_lock: self.config[TIMER][SLEEP] = False self.config[TIMER][POWEROFF] = False self.sleep_menu.sleep_button.set_selected(False) self.sleep_menu.poweroff_button.set_selected(False) self.sleep_menu.sleep_selected = False self.sleep_menu.poweroff_selected = False self.sleep_menu.sleep_button.clean_draw_update() self.sleep_menu.poweroff_button.clean_draw_update() def unselect_wake_up(self): """ Unselect wake up button """ self.wake_up_menu.button.set_selected(False) with self.timer_lock: self.config[TIMER][WAKE_UP] = False self.wake_up_menu.button_selected = False self.wake_up_menu.button.clean_draw_update() def add_screen_observers(self, update_observer, redraw_observer): """ Add screen observers :param update_observer: observer for updating the screen :param redraw_observer: observer to redraw the whole screen """ Screen.add_screen_observers(self, update_observer, redraw_observer) self.sleep_menu.add_menu_observers(update_observer, redraw_observer) self.wake_up_menu.add_menu_observers(update_observer, redraw_observer) self.navigator.add_observers(update_observer, redraw_observer)
def __init__(self, util, get_current_playlist, playlist_provider, listeners, voice_assistant): """ Initializer :param util: utility object :param listeners: file browser listeners """ self.util = util self.config = util.config self.factory = Factory(util) self.bounding_box = util.screen_rect layout = BorderLayout(self.bounding_box) layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, 0, 0) Screen.__init__(self, util, "", PERCENT_TOP_HEIGHT, voice_assistant, "file_browser_screen_title", True, layout.TOP) color_dark_light = self.config[COLORS][COLOR_DARK_LIGHT] current_folder = self.util.file_util.current_folder d = {"current_title" : current_folder} if self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] == FILE_PLAYLIST: f = self.config[FILE_PLAYBACK][CURRENT_FOLDER] p = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST] d = f + os.sep + p self.screen_title.set_text(d) rows = self.config[FILE_BROWSER_ROWS] columns = self.config[FILE_BROWSER_COLUMNS] self.filelist = None playback_mode = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] if not playback_mode: self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] = playback_mode = FILE_AUDIO if playback_mode == FILE_AUDIO or playback_mode == FILE_RECURSIVE: folder_content = self.util.load_folder_content(current_folder, rows, columns, layout.CENTER) self.filelist = Page(folder_content, rows, columns) elif playback_mode == FILE_PLAYLIST: s = State() s.folder = self.config[FILE_PLAYBACK][CURRENT_FOLDER] s.music_folder = self.config[AUDIO][MUSIC_FOLDER] s.file_name = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST] pl = self.get_filelist_items(get_current_playlist) if len(pl) == 0: pl = self.util.load_playlist(s, playlist_provider, rows, columns) else: pl = self.util.load_playlist_content(pl, rows, columns) self.filelist = Page(pl, rows, columns) self.file_menu = FileMenu(self.filelist, util, playlist_provider, (0, 0, 0), layout.CENTER, self.config[ALIGN_BUTTON_CONTENT_X]) Container.add_component(self, self.file_menu) self.file_menu.add_change_folder_listener(self.screen_title.set_text) self.file_menu.add_play_file_listener(listeners[KEY_PLAY_FILE]) listeners[GO_LEFT_PAGE] = self.file_menu.page_down listeners[GO_RIGHT_PAGE] = self.file_menu.page_up listeners[GO_USER_HOME] = self.file_menu.switch_to_user_home listeners[GO_ROOT] = self.file_menu.switch_to_root listeners[GO_TO_PARENT] = self.file_menu.switch_to_parent_folder self.navigator = Navigator(util, layout.BOTTOM, listeners, color_dark_light) left = str(self.filelist.get_left_items_number()) right = str(self.filelist.get_right_items_number()) self.navigator.left_button.change_label(left) self.navigator.right_button.change_label(right) self.file_menu.add_left_number_listener(self.navigator.left_button.change_label) self.file_menu.add_right_number_listener(self.navigator.right_button.change_label) Container.add_component(self, self.navigator) self.page_turned = False self.animated_title = True
class MenuScreen(Screen): """ Site Menu Screen. Base class for all book menu screens """ def __init__(self, util, listeners, rows, columns, voice_assistant, d=None, turn_page=None, page_in_title=True): """ Initializer :param util: utility object :param listeners: file browser listeners :param rows: menu rows :param d: dictionary with menu button flags :param turn_page: turn page callback :param util: utility object """ self.util = util self.config = util.config self.factory = Factory(util) self.bounding_box = self.config[SCREEN_RECT] self.player = None self.turn_page = turn_page self.page_in_title = page_in_title self.cache = Cache(self.util) self.layout = BorderLayout(self.bounding_box) self.layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, 0, 0) Screen.__init__(self, util, "", PERCENT_TOP_HEIGHT, voice_assistant, "menu_screen_screen_title", True, self.layout.TOP) color_dark_light = self.config[COLORS][COLOR_DARK_LIGHT] self.menu_layout = self.layout.CENTER self.menu_button_layout = self.get_menu_button_layout(d) self.img_rect = self.menu_button_layout.image_rectangle listeners[GO_LEFT_PAGE] = self.previous_page listeners[GO_RIGHT_PAGE] = self.next_page try: self.navigator = BookNavigator(util, self.layout.BOTTOM, listeners, color_dark_light, d[4]) Container.add_component(self, None) Container.add_component(self, self.navigator) except: Container.add_component(self, None) self.total_pages = 0 self.current_page = 1 self.menu = None self.loading_listeners = [] self.LOADING = util.config[LABELS][KEY_LOADING] def get_menu_button_layout(self, d): """ Return menu button layout :param d: dictionary with menu button flags :return: menu button layout """ s = State() s.show_img = True s.show_label = True button_w = int(self.menu_layout.w / d[1]) button_h = int(self.menu_layout.h / d[0]) label_padding = 2 image_padding = 4 try: self.show_author = d[2] except: self.show_author = False try: self.show_genre = d[3] except: self.show_genre = False s.bounding_box = Rect(0, 0, button_w, button_h) return MultiLineButtonLayout(s, label_padding, image_padding) def previous_page(self, state): """ Handle click on left button :param state: button state object """ if self.current_page == 1: return self.current_page -= 1 if getattr(state, "select_last", False): self.components[1].selected_index = 0 self.menu.current_page = self.current_page self.menu.selected_index = 0 self.turn_page() def next_page(self, state): """ Handle click on right button :param state: button state object """ if self.total_pages <= 1 or self.total_pages == self.current_page: return self.current_page += 1 if self.current_page > self.total_pages: self.current_page = self.total_pages self.menu.current_page = self.current_page self.menu.selected_index = 0 self.turn_page() def set_menu(self, menu): """ Set menu :param menu: menu object """ self.menu = self.components[1] = menu def set_title(self, page_num): """ Set screen title :param page_num: menu page number """ if self.total_pages == 1 or not self.page_in_title: self.screen_title.set_text(self.title) return if len(str(page_num)) <= len(str(self.total_pages)): self.screen_title.set_text(self.title + " (" + str(page_num) + ")") def reset_title(self): """ Reset screen title """ self.screen_title.set_text(self.title + " (" + str(self.current_page) + ")") def go_to_page(self, page_num): """ Handle go to page event :param page_num: menu page number """ n = int(page_num) if n > self.total_pages: n = self.total_pages if n == 0: n = 1 self.current_page = n self.menu.current_page = self.current_page self.turn_page() def get_image_from_cache(self, url): """ Return image from cache :param url: image url :return: image """ return self.cache.get_image(url) def put_image_to_cache(self, url, img): """ Put image into cache :param url: image url :param img: image """ self.cache.cache_image(img, url) def set_button_image(self, b, icon, img_y=None): """ Set button image :param b: button :param icon: image :param img_y: image Y coordinate """ bb = b.bounding_box comps = b.components im = comps[1] im.content = icon w = im.content.get_size()[0] h = im.content.get_size()[1] im.content_x = bb.x + (bb.width - w) / 2 if img_y == None: img_area_height = bb.height - ((bb.height / LINES) * 2) img_y = bb.y + (img_area_height - h) / 2 im.content_y = img_y self.components[1].clean_draw_update() def set_loading(self, name): """ Show Loading... sign :name: screen title """ b = self.config[COLORS][COLOR_DARK] f = self.config[COLORS][COLOR_BRIGHT] fs = int(self.bounding_box.h * 0.07) bb = self.menu_layout t = self.factory.create_output_text(self.LOADING, bb, b, f, fs) t.set_text(self.LOADING) self.screen_title.set_text(name) self.set_visible(True) self.add_component(t) self.clean_draw_update() self.notify_loading_listeners() def reset_loading(self): """ Remove Loading... sign """ del self.components[-1] self.notify_loading_listeners() def add_loading_listener(self, listener): """ Add loading listener :param listener: event listener """ if listener not in self.loading_listeners: self.loading_listeners.append(listener) def notify_loading_listeners(self): """ Notify all loading listeners """ for listener in self.loading_listeners: listener(None)
class FilePlayerScreen(Screen): """ File Player Screen """ def __init__(self, listeners, util, get_current_playlist, voice_assistant, player_stop=None, arrow_labels=True, active_file_button=True, show_time_control=True, show_order=True, show_info=True): """ Initializer :param listeners: screen listeners :param util: utility object :param get_current_playlist: current playlist getter :param voice_assistant: voice assistant :param player_stop: stop player function """ self.util = util self.config = util.config self.cdutil = CdUtil(util) self.factory = Factory(util) self.image_util = util.image_util self.top_height = self.config[PLAYER_SCREEN][TOP_HEIGHT_PERCENT] self.bottom_height = self.config[PLAYER_SCREEN][BOTTOM_HEIGHT_PERCENT] self.button_height = self.config[PLAYER_SCREEN][BUTTON_HEIGHT_PERCENT] self.popup_width = self.config[PLAYER_SCREEN][POPUP_WIDTH_PERCENT] self.image_location = self.config[PLAYER_SCREEN][IMAGE_LOCATION] self.stop_player = player_stop self.get_current_playlist = get_current_playlist self.show_time_control = show_time_control self.bounding_box = util.screen_rect self.layout = self.get_layout() self.voice_assistant = voice_assistant Screen.__init__(self, util, "", self.top_height, voice_assistant, "file_player_screen_title", True, self.layout.TOP) self.layout = self.get_layout() self.create_left_panel(self.layout, listeners, arrow_labels) self.create_right_panel(self.layout, listeners, arrow_labels) if not active_file_button: listeners[AUDIO_FILES] = None self.file_button = self.factory.create_file_button( self.layout.CENTER, listeners[AUDIO_FILES]) self.add_component(self.file_button) self.audio_files = self.get_audio_files() self.home_button.add_release_listener(listeners[KEY_HOME]) self.shutdown_button.add_release_listener(listeners[KEY_SHUTDOWN]) self.left_button.add_release_listener(self.go_left) self.right_button.add_release_listener(self.go_right) self.show_order = show_order self.show_info = show_info self.order_button = None self.info_button = None self.order_popup = None self.info_popup = None self.playback_order = self.config[PLAYER_SETTINGS][PLAYBACK_ORDER] self.bottom_center_layout = self.layout.BOTTOM if self.show_order or self.show_info: self.start_screensaver = listeners[SCREENSAVER] self.go_info_screen = listeners[KEY_INFO] self.add_popups() self.volume = self.factory.create_volume_control( self.bottom_center_layout) self.volume.add_slide_listener( self.get_listener(listeners, KEY_SET_VOLUME)) self.volume.add_slide_listener( self.get_listener(listeners, KEY_SET_CONFIG_VOLUME)) self.volume.add_slide_listener( self.get_listener(listeners, KEY_SET_SAVER_VOLUME)) self.volume.add_knob_listener(self.get_listener(listeners, KEY_MUTE)) self.volume_visible = False self.volume.set_visible(self.volume_visible) self.add_component(self.volume) if self.show_time_control: self.time_control = self.factory.create_time_control( self.bottom_center_layout) if KEY_SEEK in listeners.keys(): self.time_control.add_seek_listener(listeners[KEY_SEEK]) self.play_button.add_listener(PAUSE, self.time_control.pause) self.play_button.add_listener(KEY_PLAY, self.time_control.resume) if self.config[PLAYER_SETTINGS][PAUSE]: self.time_control.pause() self.add_component(self.time_control) self.left_button.add_release_listener( self.play_button.draw_default_state) self.right_button.add_release_listener( self.play_button.draw_default_state) self.play_listeners = [] self.add_play_listener(self.get_listener(listeners, KEY_PLAY)) self.current_folder = self.config[FILE_PLAYBACK][CURRENT_FOLDER] self.file_button.state.cover_art_folder = self.util.file_util.get_cover_art_folder( self.current_folder) self.playlist_size = 0 self.player_screen = True self.cd_album = None self.animated_title = True if self.order_popup: Container.add_component(self, self.order_popup) if self.info_popup: Container.add_component(self, self.info_popup) def get_layout(self): """ Get the layout of the center area of the screen for image and buttons :return: layout rectangle """ layout = BorderLayout(self.bounding_box) k = self.bounding_box.w / self.bounding_box.h percent_menu_width = (100.0 - self.top_height - self.bottom_height) / k panel_width = (100.0 - percent_menu_width) / 2.0 if self.image_location == LOCATION_CENTER: layout.set_percent_constraints(self.top_height, self.bottom_height, panel_width, panel_width) elif self.image_location == LOCATION_LEFT: layout.set_percent_constraints(self.top_height, self.bottom_height, 0, panel_width * 2) elif self.image_location == LOCATION_RIGHT: layout.set_percent_constraints(self.top_height, self.bottom_height, panel_width * 2, 0) return layout def get_panel_layout(self, layout, panel_location): """ Get the layout of the panel for buttons :param layout: layout of the whole central area :param panel_location: panel location: left or right :return: panel layout rectangle """ if self.image_location == LOCATION_CENTER: if panel_location == LOCATION_LEFT: return BorderLayout(layout.LEFT) else: return BorderLayout(layout.RIGHT) elif self.image_location == LOCATION_LEFT: r = layout.RIGHT if panel_location == LOCATION_LEFT: return BorderLayout(Rect(r.x, r.y, r.w / 2, r.h)) else: return BorderLayout( Rect(r.x + r.w / 2 + 1, r.y, r.w / 2 - 1, r.h)) elif self.image_location == LOCATION_RIGHT: r = layout.LEFT if panel_location == LOCATION_LEFT: return BorderLayout(Rect(r.x, r.y, r.w / 2, r.h)) else: return BorderLayout( Rect(r.x + r.w / 2 + 1, r.y, r.w / 2 - 1, r.h)) def add_popups(self): """ Add popup menus: playback order & info """ self.bottom_layout = BorderLayout(self.layout.BOTTOM) if self.show_order and not self.show_info: self.bottom_layout.set_percent_constraints(0, 0, self.popup_width, 0) self.order_button = self.factory.create_order_button( self.bottom_layout.LEFT, self.handle_order_button, self.playback_order) self.order_popup = self.get_order_popup(self.bounding_box) self.add_component(self.order_button) elif self.show_order and self.show_info: self.bottom_layout.set_percent_constraints(0, 0, self.popup_width, self.popup_width) self.order_button = self.factory.create_order_button( self.bottom_layout.LEFT, self.handle_order_button, self.playback_order) self.info_button = self.factory.create_info_button( self.bottom_layout.RIGHT, self.handle_info_button) self.order_popup = self.get_order_popup(self.bounding_box) self.info_popup = self.get_info_popup(self.bounding_box) self.add_component(self.order_button) self.add_component(self.info_button) elif not self.show_order and self.show_info: self.bottom_layout.set_percent_constraints(0, 0, 0, self.popup_width) self.info_button = self.factory.create_info_button( self.bottom_layout.RIGHT, self.handle_info_button) self.info_popup = self.get_info_popup(self.bounding_box) self.add_component(self.info_button) self.bottom_layout.CENTER.w -= 2 self.bottom_layout.CENTER.x += 1 self.bottom_center_layout = self.bottom_layout.CENTER def handle_order_button(self, state): """ Handle playback order button :param state: button state """ self.order_popup.set_visible(True) self.clean_draw_update() def handle_info_button(self, state): """ Handle info button :param state: button state """ self.info_popup.set_visible(True) self.clean_draw_update() def get_order_popup(self, bb): """ Create playback order popup menu :param bb: bounding box :return: popup menu """ items = [] items.append(PLAYBACK_CYCLIC) items.append(PLAYBACK_REGULAR) items.append(PLAYBACK_SINGLE_TRACK) items.append(PLAYBACK_SHUFFLE) items.append(PLAYBACK_SINGLE_CYCLIC) layout = BorderLayout(bb) layout.set_percent_constraints(self.top_height, 0, self.popup_width, 0) popup = Popup(items, self.util, layout.LEFT, self.clean_draw_update, self.handle_order_popup_selection, default_selection=self.playback_order) self.left_button.add_label_listener(popup.update_popup) return popup def get_info_popup(self, bb): """ Create info popup menu :param bb: bounding box :return: popup menu """ items = [] mode = self.config[CURRENT][MODE] items.append(CLOCK) items.append(WEATHER) items.append(LYRICS) if mode == AUDIO_FILES or mode == COLLECTION: items.append(FILE_INFO) layout = BorderLayout(bb) layout.set_percent_constraints(self.top_height, 0, 0, self.popup_width) popup = Popup(items, self.util, layout.RIGHT, self.clean_draw_update, self.handle_info_popup_selection) self.right_button.add_label_listener(popup.update_popup) return popup def handle_order_popup_selection(self, state): """ Handle playback order menu selection :param state: button state """ b = self.factory.create_order_button(self.bottom_layout.LEFT, self.handle_order_button, state.name) i = self.components.index(self.order_button) self.components[i] = b self.order_button = b self.add_button_observers(self.order_button, self.update_observer, self.redraw_observer) self.order_button.clean_draw_update() self.config[PLAYER_SETTINGS][PLAYBACK_ORDER] = state.name def handle_info_popup_selection(self, state): """ Handle info menu selection :param state: button state """ n = state.name if n == CLOCK or n == WEATHER: self.start_screensaver(n) elif n == LYRICS: a = None try: m = self.util.get_file_metadata() a = m["artist"] + " - " + m["title"] except: pass if a != None: s = State() s.album = a self.start_screensaver(n, s) else: self.start_screensaver(n) else: self.go_info_screen(state) def get_audio_files(self): """ Return the list of audio files in current folder :return: list of audio files """ files = [] if self.config[CURRENT][MODE] == CD_PLAYER: af = getattr(self, "audio_files", None) if af == None: cd_drive_name = self.config[CD_PLAYBACK][CD_DRIVE_NAME] files = self.cdutil.get_cd_tracks_summary(cd_drive_name) else: return self.audio_files else: folder = self.config[FILE_PLAYBACK][CURRENT_FOLDER] files = self.util.get_audio_files_in_folder(folder) return files def get_current_track_index(self, state=None): """ Return current track index. In case of files goes through the file list. In case of playlist takes track index from the state object. :param state: button state :return: current track index """ if self.config[CURRENT][MODE] == AUDIOBOOKS: t = None try: t = state["file_name"] except: pass if t == None: try: t = state["current_title"] except: pass if t == None: try: t = state except: pass for i, f in enumerate(self.audio_files): try: s = f["file_name"] except: pass if getattr(f, "file_name", None): s = getattr(f, "file_name", None) if s.endswith(t): return i return 0 mode = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] if state and mode == FILE_PLAYLIST: try: n = state["Track"] if n: return int(n) - 1 except: pass if self.config[CURRENT][MODE] == CD_PLAYER: cmp = int(self.config[CD_PLAYBACK][CD_TRACK]) - 1 else: cmp = self.config[FILE_PLAYBACK][CURRENT_FILE] if state and isinstance(state, State): cmp = state.file_name for f in self.audio_files: if self.config[CURRENT][MODE] == CD_PLAYER: if f.index == cmp: return f.index else: if f.file_name == cmp: return f.index return 0 def stop_recursive_playback(self): """ Stop recursive playback """ self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] = FILE_AUDIO self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST] = None self.config[FILE_PLAYBACK][CURRENT_TRACK_TIME] = None self.stop_timer() if self.show_time_control: self.time_control.stop_thread() self.time_control.reset() if self.stop_player != None: self.stop_player() def go_left(self, state): """ Switch to the previous track :param state: not used state object """ if getattr(self, "current_track_index", None) == None: return filelist_size = self.get_filelist_size() if self.config[FILE_PLAYBACK][ CURRENT_FILE_PLAYBACK_MODE] == FILE_RECURSIVE: self.stop_recursive_playback() return if self.current_track_index == 0: self.current_track_index = filelist_size - 1 else: self.current_track_index -= 1 self.change_track(self.current_track_index) def go_right(self, state): """ Switch to the next track :param state: not used state object """ if getattr(self, "current_track_index", None) == None: return filelist_size = self.get_filelist_size() if self.config[FILE_PLAYBACK][ CURRENT_FILE_PLAYBACK_MODE] == FILE_RECURSIVE and self.current_track_index == filelist_size - 1: self.stop_timer() if self.show_time_control: self.time_control.stop_thread() self.recursive_change_folder() self.current_track_index = 0 self.change_track(self.current_track_index) self.file_button.clean_draw_update() return if self.current_track_index == filelist_size - 1: self.current_track_index = 0 else: self.current_track_index += 1 self.change_track(self.current_track_index) def get_filelist_size(self): """ Return the file list size :return: file list size """ if self.audio_files: return len(self.audio_files) else: return 0 def change_track(self, track_index): """ Change track :param track_index: index track """ a = [AUDIOBOOKS, CD_PLAYER] m = self.config[CURRENT][MODE] if not (m in a): self.config[FILE_PLAYBACK][CURRENT_FILE] = self.get_filename( track_index) self.stop_timer() time.sleep(0.3) s = State() if m == FILE_PLAYBACK: s.playback_mode = self.config[FILE_PLAYBACK][ CURRENT_FILE_PLAYBACK_MODE] s.playlist_track_number = track_index s.index = track_index s.source = ARROW_BUTTON s.file_name = self.get_filename(track_index) if self.cd_album != None: s.album = self.cd_album if self.config[CURRENT][MODE] == AUDIO_FILES: folder = self.current_folder if not folder.endswith(os.sep): folder += os.sep s.url = folder + s.file_name self.set_current(True, s) def stop_timer(self): """ Stop time control timer """ if self.show_time_control: self.time_control.stop_timer() def get_filename(self, index): """ Get filename by index :param index: file index :return: filename """ for b in self.audio_files: if b.index == index: return b.file_name return "" def is_valid_mode(self): current_mode = self.config[CURRENT][MODE] modes = [AUDIO_FILES, AUDIOBOOKS, CD_PLAYER] if current_mode in modes: return True else: return False def update_arrow_button_labels(self, state=None): """ Update left/right arrow button labels :param state: state object representing current track """ if (not self.is_valid_mode()) or (not self.screen_title.active): return self.set_current_track_index(state) left = self.current_track_index right = 0 if self.audio_files and len(self.audio_files) > 1: right = len(self.audio_files) - self.current_track_index - 1 self.left_button.change_label(str(left)) self.right_button.change_label(str(right)) def set_current_track_index(self, state): """ Set current track index :param state: state object representing current track """ if not self.is_valid_mode(): return if self.config[CURRENT][MODE] == CD_PLAYER and getattr( state, "cd_track_id", None): self.config[CD_PLAYBACK][CD_TRACK] = state["cd_track_id"] self.current_track_index = 0 if self.playlist_size == 1: return if not self.audio_files: self.audio_files = self.get_audio_files() if not self.audio_files: return i = self.get_current_track_index(state) self.current_track_index = i def create_left_panel(self, layout, listeners, arrow_labels): """ Create left side buttons panel :param layout: panel layout :param listeners: button listeners :param arrow_labels: show arrow label or not """ panel_layout = self.get_panel_layout(layout, LOCATION_LEFT) panel_layout.set_percent_constraints(self.button_height, self.button_height, 0, 0) self.left_button = self.factory.create_left_button( panel_layout.CENTER, '', 40, 100, arrow_labels) panel_layout.TOP.y += 1 panel_layout.TOP.h -= 2 self.shutdown_button = self.factory.create_shutdown_button( panel_layout.TOP) panel_layout.BOTTOM.h += 1 self.home_button = self.factory.create_button(KEY_HOME, KEY_HOME, panel_layout.BOTTOM, image_size_percent=36) panel = Container(self.util, layout.LEFT) panel.add_component(self.shutdown_button) panel.add_component(self.left_button) panel.add_component(self.home_button) self.add_component(panel) def create_right_panel(self, layout, listeners, arrow_labels): """ Create right side buttons panel :param layout: panel layout :param listeners: button listeners :param arrow_labels: show arrow label or not """ panel_layout = self.get_panel_layout(layout, LOCATION_RIGHT) panel_layout.set_percent_constraints(self.button_height, self.button_height, 0, 0) panel_layout.TOP.y += 1 panel_layout.TOP.h -= 2 self.play_button = self.factory.create_play_pause_button( panel_layout.TOP, self.get_listener(listeners, KEY_PLAY_PAUSE)) self.right_button = self.factory.create_right_button( panel_layout.CENTER, '', 40, 100, arrow_labels) panel_layout.BOTTOM.h += 1 self.time_volume_button = self.factory.create_time_volume_button( panel_layout.BOTTOM, self.toggle_time_volume) panel = Container(self.util, layout.RIGHT) panel.add_component(self.play_button) panel.add_component(self.right_button) panel.add_component(self.time_volume_button) self.add_component(panel) def get_listener(self, listeners, name): """ Return listener :param listeners: all listeners :param name: listener name :return: listener if avaialble, None - otherwise """ if name in listeners.keys(): return listeners[name] else: return None def toggle_time_volume(self): """ Switch between time and volume controls """ if self.volume_visible: self.volume.set_visible(False) if self.show_time_control: self.time_control.set_visible(True) self.volume_visible = False else: volume_level = int(self.config[PLAYER_SETTINGS][VOLUME]) self.volume.set_position(volume_level) self.volume.update_position() self.volume.set_visible(True) if self.show_time_control: self.time_control.set_visible(False) self.volume_visible = True self.clean_draw_update() if hasattr(self, "time_control"): self.time_control.slider.clean_draw_update() def eject_cd(self, state): """ Eject CD :param state: button state object """ self.audio_files = [] self.screen_title.set_text(" ") self.update_arrow_button_labels(state) if self.show_time_control: self.time_control.reset() self.cd_album = None self.set_cd_album_art_image() def set_current(self, new_track=False, state=None): """ Set current file or playlist :param new_track: True - new audio file :param state: button state """ self.cd_album = getattr(state, "album", None) if self.config[CURRENT][MODE] == AUDIO_FILES: if getattr(state, "url", None) is None: state.url = None state.full_screen_image = self.set_audio_file_image(state.url) elif self.config[CURRENT][MODE] == CD_PLAYER and getattr( state, "source", None) != INIT: state.full_screen_image = self.set_cd_album_art_image() state.image_base = self.file_button.components[1].content config_volume_level = int(self.config[PLAYER_SETTINGS][VOLUME]) if state: if self.config[VOLUME_CONTROL][ VOLUME_CONTROL_TYPE] == VOLUME_CONTROL_TYPE_PLAYER: state.volume = config_volume_level else: state.volume = None self.set_audio_file(new_track, state) if self.volume.get_position() != config_volume_level: self.volume.set_position(config_volume_level) self.volume.update_position() def set_audio_file_image(self, url=None, folder=None): """ Set audio file image :param url: audio file name :param folder: folder name :return: image """ if folder: f = folder else: f = self.config[FILE_PLAYBACK][CURRENT_FOLDER] if not f: return None img_tuple = self.image_util.get_audio_file_icon( f, self.bounding_box, url) self.set_file_button(img_tuple) self.file_button.clean_draw_update() return img_tuple[1] def set_cd_album_art_image(self): """ Set CD album art image """ img_tuple = self.image_util.get_cd_album_art(self.cd_album, self.bounding_box) if img_tuple == None: return None self.set_file_button(img_tuple) self.file_button.clean_draw_update() return img_tuple[1] def set_file_button(self, img_tuple): """ Set image in file button :param img_tuple: tuple where first element is image location, second element image itself """ full_screen_image = img_tuple[1] self.file_button.state.full_screen_image = full_screen_image scale_ratio = self.image_util.get_scale_ratio( (self.layout.CENTER.w, self.layout.CENTER.h), full_screen_image) img = self.image_util.scale_image(full_screen_image, scale_ratio) self.file_button.components[1].content = img self.file_button.state.icon_base = img self.file_button.components[ 1].image_filename = self.file_button.state.image_filename = img_tuple[ 0] self.file_button.components[1].content_x = self.layout.CENTER.x + ( self.layout.CENTER.w - img.get_size()[0]) / 2 if self.layout.CENTER.h > img.get_size()[1]: self.file_button.components[ 1].content_y = self.layout.CENTER.y + int( (self.layout.CENTER.h - img.get_size()[1]) / 2) + 1 else: self.file_button.components[1].content_y = self.layout.CENTER.y def set_audio_file(self, new_track, s=None): """ Set new audio file :param new_track: True - new audio file "param s" button state object """ state = State() if s: state.playback_mode = getattr(s, "playback_mode", FILE_AUDIO) state.playlist_track_number = getattr(s, "playlist_track_number", None) if self.config[CURRENT][MODE] == CD_PLAYER and getattr( s, "source", None) != INIT: image_base = getattr(s, "image_base", None) if image_base != None: state.image_base = image_base else: m = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] if m: state.playback_mode = m else: state.playback_mode = FILE_AUDIO if self.config[CURRENT][MODE] == AUDIO_FILES: self.current_folder = self.config[FILE_PLAYBACK][CURRENT_FOLDER] if not self.current_folder: return state.folder = self.current_folder state.file_name = self.config[FILE_PLAYBACK][CURRENT_FILE] if state.folder[-1] == os.sep: state.folder = state.folder[:-1] if os.sep in state.file_name: state.url = "\"" + state.file_name + "\"" else: state.url = "\"" + state.folder + os.sep + state.file_name + "\"" if s.url == None and state.url != None: state.full_screen_image = self.set_audio_file_image( state.url.replace('\"', "")) state.music_folder = self.config[AUDIO][MUSIC_FOLDER] elif self.config[CURRENT][MODE] == CD_PLAYER: state.file_name = s.file_name state.url = getattr(s, "url", s.file_name) parts = s.file_name.split() self.config[CD_PLAYBACK][CD_DRIVE_NAME] = parts[0][len("cdda:///" ):] id = self.cdutil.get_cd_drive_id_by_name( self.config[CD_PLAYBACK][CD_DRIVE_NAME]) self.config[CD_PLAYBACK][CD_DRIVE_ID] = int(id) self.config[CD_PLAYBACK][CD_TRACK] = int(parts[1].split("=")[1]) self.config[PLAYER_SETTINGS][PAUSE] = False state.mute = self.config[PLAYER_SETTINGS][MUTE] state.pause = self.config[PLAYER_SETTINGS][PAUSE] self.play_button.draw_default_state(None) state.file_type = FILE_AUDIO state.dont_notify = True state.source = FILE_AUDIO if self.config[FILE_PLAYBACK][ CURRENT_FILE_PLAYBACK_MODE] == FILE_AUDIO or self.config[ CURRENT][MODE] == CD_PLAYER: self.audio_files = self.get_audio_files() elif self.config[FILE_PLAYBACK][ CURRENT_FILE_PLAYBACK_MODE] == FILE_PLAYLIST: state.file_name = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST] self.load_playlist(state) state.file_name = self.config[FILE_PLAYBACK][CURRENT_FILE] self.audio_files = self.get_audio_files_from_playlist() state.playback_mode = FILE_PLAYLIST n = getattr(s, "file_name", None) if n: state.file_name = n try: state.playlist_track_number = int(state.file_name) - 1 except: state.playlist_track_number = self.get_current_track_index( state) source = None if s: source = getattr(s, "source", None) if new_track: tt = 0.0 else: if self.config[CURRENT][MODE] == CD_PLAYER: tt = self.config[CD_PLAYBACK][CD_TRACK_TIME] else: tt = self.config[FILE_PLAYBACK][CURRENT_TRACK_TIME] if (isinstance(tt, str) and len(tt) != 0) or ( isinstance(tt, float) or (source and source == RESUME)) or isinstance(tt, int): state.track_time = tt if self.show_time_control: self.time_control.slider.set_position(0) if self.file_button and self.file_button.components[ 1] and self.file_button.components[1].content: state.icon_base = self.file_button.components[1].content if s and hasattr(s, "volume"): state.volume = s.volume if self.config[CURRENT][MODE] == CD_PLAYER and s and getattr( s, "source", None) == INIT: try: self.cd_album = self.util.cd_titles[self.config[CD_PLAYBACK] [CD_DRIVE_NAME]] self.set_cd_album_art_image() state.image_base = self.file_button.components[1].content except: self.cd_album = None if getattr(s, "full_screen_image", None) != None: state.full_screen_image = s.full_screen_image song_name = self.get_song_name(s) if song_name != None: state.album = song_name self.notify_play_listeners(state) def get_song_name(self, state): """ Get song name in the format: Artist - Song name :param state: state object """ album = getattr(state, "album", None) if album == None: return None artist = album if "/" in album: artist = album.split("/")[0].strip() if artist == None or len(artist) == 0: return None name = getattr(state, "l_name", None) file_name = getattr(state, "file_name", None) if name == None: if file_name == None: return None else: if file_name.startswith("cdda:"): id = int(file_name.split("=")[1].strip()) name = self.audio_files[id - 1].name else: name = file_name pos = name.find(".") if pos != -1: tokens = name.split(".") if tokens[0].strip().isdigit(): name = name[pos + 1:].strip() if name == None or len(name) == 0: return None else: return artist + " - " + name def get_audio_files_from_playlist(self): """ Call player for files in the playlist :return: list of files from playlist """ playlist = self.get_current_playlist() files = [] if playlist: for n in range(len(playlist)): st = State() st.index = st.comparator_item = n st.file_type = FILE_AUDIO st.file_name = playlist[n] files.append(st) return files def restore_current_folder(self, state=None): """ Set current folder in config object :param state: not used """ self.config[FILE_PLAYBACK][CURRENT_FOLDER] = self.current_folder def set_audio_file_playlist(self, index): """ Set file in playlist :param index: file index in playlist """ state = State() state.playback_mode = FILE_PLAYLIST state.playlist_track_number = index state.file_type = FILE_AUDIO self.current_folder = self.config[FILE_PLAYBACK][CURRENT_FOLDER] self.notify_play_listeners(state) def go_back(self): """ Go back """ pass def recursive_change_folder(self): start_folder = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST] current_folder = self.config[FILE_PLAYBACK][CURRENT_FOLDER] f = self.util.file_util.get_next_folder_with_audio_files( start_folder, current_folder) if f == None or (f != None and f[0] == None): self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] = FILE_AUDIO self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST] = None return False self.config[FILE_PLAYBACK][CURRENT_FOLDER] = f[0] self.config[FILE_PLAYBACK][CURRENT_FILE] = f[1] self.config[FILE_PLAYBACK][CURRENT_TRACK_TIME] = None state = State() state.file_type = FOLDER state.url = f[0] state.long_press = True state.playback_mode = FILE_RECURSIVE self.current_track_index = 0 state.dont_notify = True self.audio_files = self.get_audio_files() self.recursive_notifier(f[0]) return True def end_of_track(self): """ Handle end of track """ if not self.screen_title.active: return i = getattr(self, "current_track_index", None) if i == None: return self.stop_timer() mode = self.config[CURRENT][MODE] if mode == RADIO or mode == STREAM or not self.audio_files: return if self.show_time_control: self.time_control.stop_thread() playback_mode = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] if playback_mode == FILE_RECURSIVE: if self.current_track_index == (len(self.audio_files) - 1): if not self.recursive_change_folder(): self.stop_recursive_playback() return else: index = 0 else: index = self.current_track_index + 1 else: index = self.get_next_index() if index == None: self.time_control.stop_thread() self.time_control.reset() return else: self.current_track_index = index if mode == AUDIO_FILES: self.config[FILE_PLAYBACK][CURRENT_TRACK_TIME] = None elif mode == AUDIOBOOKS: self.config[AUDIOBOOKS][BROWSER_BOOK_TIME] = None self.change_track(index) if playback_mode == FILE_RECURSIVE: self.file_button.clean_draw_update() def get_next_index(self): """ Return next file index :return: file index or None """ order = self.config[PLAYER_SETTINGS][PLAYBACK_ORDER] n = self.current_track_index last = len(self.audio_files) - 1 if order == PLAYBACK_SINGLE_TRACK: return None if order == PLAYBACK_SINGLE_CYCLIC: return n if order == PLAYBACK_CYCLIC: if n == last: return 0 else: return n + 1 elif order == PLAYBACK_REGULAR: if n == last: return None else: return n + 1 elif order == PLAYBACK_SHUFFLE: if len(self.audio_files) == 1: return n else: return random.randint(0, last) def set_playlist_size(self, size): """ Set playlist size :param size: playlist size """ self.playlist_size = size self.stop_timer() def set_visible(self, flag): """ Set visibility flag :param flag: True - screen visible, False - screen invisible """ Container.set_visible(self, flag) if flag: if self.volume_visible: self.volume.set_visible(True) if self.show_time_control: self.time_control.set_visible(False) else: self.volume.set_visible(False) if self.show_time_control: self.time_control.set_visible(True) def add_play_listener(self, listener): """ Add play listener :param listener: event listener """ if listener not in self.play_listeners: self.play_listeners.append(listener) def notify_play_listeners(self, state): """ Notify all play listeners :param state: button state """ if not self.screen_title.active: return m = getattr(state, "playback_mode", None) if m != None and m != FILE_PLAYLIST: state.icon_base = self.file_button.state.icon_base folder = getattr(state, "folder", None) if folder: state.cover_art_folder = self.util.file_util.get_cover_art_folder( state.folder) for listener in self.play_listeners: listener(state) def enable_player_screen(self, flag): """ Enable player screen :param flag: enable/disable flag """ self.screen_title.active = flag if self.show_time_control: self.time_control.active = flag def add_screen_observers(self, update_observer, redraw_observer, start_time_control, stop_time_control, title_to_json): """ Add screen observers :param update_observer: observer for updating the screen :param redraw_observer: observer to redraw the whole screen :param start_time_control: :param stop_time_control: :param title_to_json: """ self.update_observer = update_observer self.redraw_observer = redraw_observer Screen.add_screen_observers(self, update_observer, redraw_observer, title_to_json) self.add_button_observers(self.shutdown_button, update_observer, redraw_observer=None) self.shutdown_button.add_cancel_listener(redraw_observer) self.screen_title.add_listener(redraw_observer) self.add_button_observers(self.play_button, update_observer, redraw_observer=None) self.add_button_observers(self.home_button, update_observer, redraw_observer) if self.order_button: self.add_button_observers(self.order_button, update_observer, redraw_observer) if self.info_button: self.add_button_observers(self.info_button, update_observer, redraw_observer) self.add_button_observers(self.left_button, update_observer, redraw_observer=None) self.left_button.add_label_listener(update_observer) self.add_button_observers(self.right_button, update_observer, redraw_observer=None) self.right_button.add_label_listener(update_observer) self.volume.add_slide_listener(update_observer) self.volume.add_knob_listener(update_observer) self.volume.add_press_listener(update_observer) self.volume.add_motion_listener(update_observer) self.add_button_observers(self.file_button, update_observer, redraw_observer, press=False, release=False) if self.order_popup: self.order_popup.add_menu_observers(update_observer, redraw_observer) if self.info_popup: self.info_popup.add_menu_observers(update_observer, redraw_observer) if self.show_time_control: self.add_button_observers(self.time_volume_button, update_observer, redraw_observer, release=False) self.time_control.web_seek_listener = update_observer if start_time_control: self.time_control.add_start_timer_listener(start_time_control) if stop_time_control: self.time_control.add_stop_timer_listener(stop_time_control) self.time_control.slider.add_slide_listener(update_observer) self.time_control.slider.add_knob_listener(update_observer) self.time_control.slider.add_press_listener(update_observer) self.time_control.slider.add_motion_listener(update_observer)
class ButtonLayout(object): """ Layout which arranges button components (icon and label) """ def __init__(self, state): """ Initializer. Define bounding boxes for button label (if any) and icon (if any) :param state: button state """ s = state bb = s.bounding_box self.show_image = getattr(s, "show_img", None) self.show_label = getattr(s, "show_label", False) self.image_location = getattr(s, "image_location", CENTER) self.label_location = getattr(s, "label_location", BOTTOM) self.image_area_percent = getattr(s, "image_area_percent", 0) self.label_area_percent = getattr(s, "label_area_percent", 0) self.layout = BorderLayout(bb) top = bottom = left = right = 0 self.image_rectangle = self.label_rectangle = None if self.show_image and self.show_label: if self.image_location == TOP and self.label_location == CENTER: top = self.image_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.TOP self.label_rectangle = self.layout.CENTER elif self.image_location == BOTTOM and self.label_location == CENTER: bottom = self.image_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.BOTTOM self.label_rectangle = self.layout.CENTER elif self.image_location == CENTER and self.label_location == TOP: top = self.label_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.CENTER self.label_rectangle = self.layout.TOP elif self.image_location == CENTER and self.label_location == BOTTOM: bottom = self.label_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.CENTER self.label_rectangle = self.layout.BOTTOM elif self.image_location == LEFT and self.label_location == CENTER: left = self.image_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.LEFT self.label_rectangle = self.layout.CENTER elif self.image_location == RIGHT and self.label_location == CENTER: right = self.image_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.RIGHT self.label_rectangle = self.layout.CENTER elif self.image_location == CENTER and self.label_location == LEFT: left = self.label_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.CENTER self.label_rectangle = self.layout.LEFT elif self.image_location == CENTER and self.label_location == RIGHT: right = self.label_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.CENTER self.label_rectangle = self.layout.RIGHT elif self.show_image and not self.show_label: self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.CENTER elif not self.show_image and self.show_label: self.layout.set_percent_constraints(top, bottom, left, right) self.label_rectangle = self.layout.CENTER def get_image_rectangle(self): """ Return button image bounding box :return: image bounding box """ return self.image_rectangle def get_label_rectangle(self): """ Return button label bounding box :return: label bounding box """ return self.label_rectangle
def __init__(self, util, listeners, voice_assistant): """ Initializer :param util: utility object :param listeners: listeners :param voice_assistant: voice assistant """ self.util = util self.config = util.config self.listeners = listeners self.factory = Factory(util) self.podcasts_util = util.get_podcasts_util() self.bounding_box = util.screen_rect layout = BorderLayout(self.bounding_box) layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, 0, 0) d = [MENU_ROWS_PODCASTS, MENU_COLUMNS_PODCASTS] MenuScreen.__init__(self, util, listeners, MENU_ROWS_PODCASTS, MENU_COLUMNS_PODCASTS, voice_assistant, d, self.turn_page, page_in_title=False, show_loading=True) self.title = self.config[LABELS][PODCASTS] m = self.create_podcast_menu_button font_size = int( ((self.menu_layout.h / MENU_ROWS_PODCASTS) / 100) * FONT_HEIGHT) self.podcasts_menu = MultiPageMenu(util, self.next_page, self.previous_page, self.set_title, self.reset_title, self.go_to_page, m, MENU_ROWS_PODCASTS, MENU_COLUMNS_PODCASTS, None, (0, 0, 0, 0), self.menu_layout, align=ALIGN_CENTER, font_size=font_size) self.set_menu(self.podcasts_menu) self.navigator = PodcastNavigator(self.util, self.layout.BOTTOM, listeners, PAGE_SIZE_PODCASTS + 1) self.add_component(self.navigator) url = self.config[PODCASTS][PODCAST_URL] if url and len(url) > 0: self.current_page = self.podcasts_util.get_podcast_page( url, PAGE_SIZE_PODCASTS) else: self.current_page = 1 self.animated_title = True
class ButtonLayout(object): """ Layout which arranges button components (icon and label) """ def __init__(self, state): """ Initializer. Define bounding boxes for button label (if any) and icon (if any) :param state: button state """ s = state bb = s.bounding_box self.show_image = getattr(s, "show_img", None) self.show_label = getattr(s, "show_label", False) self.image_location = getattr(s, "image_location", CENTER) self.label_location = getattr(s, "label_location", BOTTOM) self.image_area_percent = getattr(s, "image_size_percent", 0) * 100 self.label_area_percent = getattr(s, "label_area_percent", 0) self.layout = BorderLayout(bb) top = bottom = left = right = 0 self.image_rectangle = self.label_rectangle = None if self.show_image and self.show_label: if self.image_location == TOP and self.label_location == CENTER: top = self.image_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.TOP self.label_rectangle = self.layout.CENTER elif self.image_location == BOTTOM and self.label_location == CENTER: bottom = self.image_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.BOTTOM self.label_rectangle = self.layout.CENTER elif self.image_location == CENTER and self.label_location == TOP: top = self.label_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.CENTER self.label_rectangle = self.layout.TOP elif self.image_location == CENTER and self.label_location == BOTTOM: bottom = self.label_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.CENTER self.label_rectangle = self.layout.BOTTOM elif self.image_location == LEFT and self.label_location == CENTER: left = self.image_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.LEFT self.label_rectangle = self.layout.CENTER elif self.image_location == RIGHT and self.label_location == CENTER: right = self.image_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.RIGHT self.label_rectangle = self.layout.CENTER elif self.image_location == CENTER and self.label_location == LEFT: left = self.label_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.CENTER self.label_rectangle = self.layout.LEFT elif self.image_location == CENTER and self.label_location == RIGHT: right = self.label_area_percent self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.CENTER self.label_rectangle = self.layout.RIGHT elif self.show_image and not self.show_label: self.layout.set_percent_constraints(top, bottom, left, right) self.image_rectangle = self.layout.CENTER elif not self.show_image and self.show_label: self.layout.set_percent_constraints(top, bottom, left, right) self.label_rectangle = self.layout.CENTER def get_image_rectangle(self): """ Return button image bounding box :return: image bounding box """ return self.image_rectangle def get_label_rectangle(self): """ Return button label bounding box :return: label bounding box """ return self.label_rectangle
class Screen(Container): """ Base class for all screens. Extends Container class """ def __init__(self, util, title_key, percent_bottom_height=0, voice_assistant=None, screen_title_name="screen_title", create_dynamic_title=False, title_layout=None, title=None, bgr=None): """ Initializer :param util: utility object :param title_key: the resource bundle key for the screen title """ self.util = util self.factory = Factory(util) self.config = util.config self.bounding_box = util.screen_rect if title_key: self.name = title_key else: self.name = "tmp" bg = self.util.get_background(self.name, bgr) self.bgr_type = bg[0] self.bgr = bg[1] self.bgr_key = bg[5] Container.__init__(self, util, bounding_box=self.bounding_box, background=bg[1], content=bg[2], image_filename=bg[3]) self.original_image_filename = None try: self.original_image_filename = bg[4].replace("\\", "/") except: pass self.layout = BorderLayout(util.screen_rect) self.layout.set_percent_constraints(PERCENT_TOP_HEIGHT, percent_bottom_height, 0, 0) self.voice_assistant = voice_assistant self.font_size = int((self.layout.TOP.h * PERCENT_TITLE_FONT) / 100.0) b = self.config[BACKGROUND][HEADER_BGR_COLOR] c = self.config[COLORS][COLOR_CONTRAST] t_layout = self.layout.TOP if title_layout: t_layout = title_layout if create_dynamic_title: self.screen_title = self.factory.create_dynamic_text( screen_title_name, t_layout, b, c, self.font_size) else: self.screen_title = self.factory.create_output_text( screen_title_name, t_layout, b, c, self.font_size) if title: self.screen_title.set_text(title) else: if title_key and len(title_key) > 0: try: label = self.config[LABELS][title_key] self.screen_title.set_text(label) except: pass if voice_assistant: self.screen_title.add_select_listener(self.handle_voice_assistant) self.layout.TOP.w += 1 self.voice_command = self.factory.create_output_text( "voice_command", self.layout.TOP, b, c, self.font_size) self.voice_command.add_select_listener(self.handle_voice_assistant) self.voice_assistant.add_text_recognized_listener( self.text_recognized) self.voice_assistant.assistant.add_start_conversation_listener( self.start_conversation) self.voice_assistant.assistant.add_stop_conversation_listener( self.stop_conversation) if voice_assistant and voice_assistant.is_running(): self.title_selected = False else: self.title_selected = True self.draw_title_bar() self.player_screen = False self.update_web_observer = None self.update_web_title = None self.loading_listeners = [] self.LOADING = util.config[LABELS][KEY_LOADING] self.animated_title = False def add_component(self, c): """ Add screen component :param c: component to add """ if c: c.set_parent_screen(self) Container.add_component(self, c) def draw_title_bar(self): """ Draw title bar on top of the screen """ if len(self.components) != 0 and self.title_selected: self.add_component(self.voice_command) self.title_selected = False elif len(self.components) != 0 and not self.title_selected: self.add_component(self.screen_title) self.title_selected = True elif len(self.components) == 0 and self.title_selected: self.add_component(self.screen_title) elif len(self.components) == 0 and not self.title_selected: self.voice_command.set_text( self.config[LABELS][KEY_WAITING_FOR_COMMAND]) self.add_component(self.voice_command) def handle_voice_assistant(self, state=None): """ Start/Stop voice assistant :state: not used """ if self.title_selected: self.voice_assistant.start() else: self.voice_assistant.stop() def text_recognized(self, text): """ Handle text recognized event :text: recognized text """ c = self.config[LABELS][KEY_VA_COMMAND] + " " + text self.voice_command.set_text(c) self.voice_command.clean_draw_update() time.sleep(self.config[VOICE_ASSISTANT][VOICE_COMMAND_DISPLAY_TIME]) if self.update_web_title: self.update_web_title(self.voice_command) def start_conversation(self, event): """ Start voice conversation :event: not used """ if self.visible: self.voice_command.set_visible(True) self.voice_command.set_text_no_draw( self.config[LABELS][KEY_WAITING_FOR_COMMAND]) self.components[0] = self.voice_command self.title_selected = False if self.visible: self.voice_command.clean_draw_update() if self.update_web_observer: self.update_web_observer() def stop_conversation(self): """ Stop voice conversation """ if self.visible: self.screen_title.set_visible(True) self.components[0] = self.screen_title self.title_selected = True if self.visible: self.screen_title.clean_draw_update() if self.update_web_observer: self.update_web_observer() def add_menu(self, menu): """ Add menu to the screen :param menu: the menu to add """ self.menu = menu self.add_component(menu) def enable_player_screen(self, flag): """ Enable player screen :param flag: enable/disable flag """ pass def exit_screen(self): """ Complete actions required to save screen state """ self.set_visible(False) def add_screen_observers(self, update_observer, redraw_observer, title_to_json=None): """ Add screen observers. :param update_observer: observer for updating the screen :param redraw_observer: observer to redraw the whole screen """ self.update_web_observer = redraw_observer self.update_web_title = title_to_json def set_loading(self, name=None, menu_bb=None, text=None): """ Show Loading... sign :param name: screen title :param menu_bb: menu bounding box :param text: screen text """ b = self.config[BACKGROUND][MENU_BGR_COLOR] f = self.config[COLORS][COLOR_BRIGHT] fs = int(self.bounding_box.h * 0.07) if menu_bb != None: bb = menu_bb else: bb = self.bounding_box bb = Rect(bb.x, bb.y + 1, bb.w, bb.h - 1) t = self.factory.create_output_text(self.LOADING, bb, b, f, fs) if not text: t.set_text_no_draw(self.LOADING) else: t.set_text_no_draw(text) if name != None: self.screen_title.set_text(name) self.set_visible(True) self.add_component(t) if getattr(self, "menu", None) != None: self.menu.buttons = {} self.menu.components = [] self.clean_draw_update() self.notify_loading_listeners() def reset_loading(self): """ Remove Loading label """ del self.components[-1] self.notify_loading_listeners() def add_loading_listener(self, listener): """ Add loading listener :param listener: event listener """ if listener not in self.loading_listeners: self.loading_listeners.append(listener) def notify_loading_listeners(self): """ Notify all loading listeners """ for listener in self.loading_listeners: listener(None)
class PodcastEpisodesScreen(MenuScreen): """ Podcast Episodes Screen """ def __init__(self, util, listeners, voice_assistant, state): """ Initializer :param util: utility object :param listeners: file browser listeners :param voice_assistant: voice assistant :param state: button state """ self.util = util self.config = util.config self.podcasts_util = util.get_podcasts_util() self.listeners = listeners self.factory = Factory(util) self.bounding_box = util.screen_rect self.layout = BorderLayout(self.bounding_box) self.layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, 0, 0) d = [MENU_ROWS_EPISODES, MENU_COLUMNS_EPISODES] MenuScreen.__init__(self, util, listeners, MENU_ROWS_EPISODES, MENU_COLUMNS_EPISODES, voice_assistant, d, self.turn_page, page_in_title=False) if hasattr(state, "podcast_url"): podcast_url = state.podcast_url self.title = self.podcasts_util.summary_cache[podcast_url].name else: self.title = state.name m = self.create_episode_menu_button font_size = int( ((self.menu_layout.h / MENU_ROWS_EPISODES) / 100) * FONT_HEIGHT) self.episodes_menu = MultiPageMenu(util, self.next_page, self.previous_page, self.set_title, self.reset_title, self.go_to_page, m, MENU_ROWS_EPISODES, MENU_COLUMNS_EPISODES, None, (0, 0, 0, 0), self.menu_layout, align=ALIGN_CENTER, font_size=font_size) self.set_menu(self.episodes_menu) self.total_pages = PAGE_SIZE_EPISODES * 2 self.episodes = [] self.navigator = EpisodeNavigator(self.util, self.layout.BOTTOM, listeners, self.total_pages) self.add_component(self.navigator) self.current_page = 1 self.save_episode_listeners = [] self.animated_title = True def create_episode_menu_button(self, s, constr, action, scale, font_size): """ Create podcast episode menu button :param s: button state :param constr: scaling constraints :param action: button event listener :param scale: True - scale images, False - don't scale images :param font_size: label font height in pixels :return: genre menu button """ s.bounding_box = constr s.img_x = None s.img_y = None s.auto_update = True s.show_bgr = True s.show_img = True s.show_label = True s.image_location = LEFT s.label_location = CENTER s.label_area_percent = 30 s.image_size_percent = 0.12 s.text_color_normal = self.config[COLORS][COLOR_BRIGHT] s.text_color_selected = self.config[COLORS][COLOR_CONTRAST] s.text_color_disabled = self.config[COLORS][COLOR_MEDIUM] s.text_color_current = s.text_color_normal s.scale = scale s.source = "episode_menu" s.v_align = V_ALIGN_TOP s.h_align = H_ALIGN_LEFT s.v_offset = (constr.h / 100) * 5 s.bgr = self.config[BACKGROUND][MENU_BGR_COLOR] s.image_area_percent = ICON_AREA s.fixed_height = font_size button = EpisodeButton(self.util, s) button.add_release_listener(action) if not getattr(s, "enabled", True): button.set_enabled(False) elif getattr(s, "icon_base", False) and not getattr(s, "scaled", False): button.components[1].content = s.icon_base button.scaled = scale return button def set_current(self, state): """ Set current state :param state: button state """ if not self.util.connected_to_internet and len( self.episodes) == 0 and not hasattr(state, "url"): return if state.name == KEY_BACK or (getattr(state, "source", None) == FILE_BUTTON and len(self.episodes) != 0): self.episodes_menu.clean_draw_update() return if getattr(state, "podcast_url", None) != None: self.episodes = self.podcasts_util.get_episodes(state.podcast_url) if len(self.episodes) != 0: self.total_pages = math.ceil( len(self.episodes) / PAGE_SIZE_EPISODES) self.turn_page(state) return self.set_loading(self.title) if state.name != self.title or len(self.episodes) == 0: self.title = state.name if self.util.connected_to_internet: self.episodes = self.podcasts_util.get_episodes(state.url) else: self.episodes = self.podcasts_util.get_episodes_from_disk( state.url) self.total_pages = math.ceil(len(self.episodes) / PAGE_SIZE_EPISODES) self.turn_page(state) self.reset_loading() def turn_page(self, state=None): """ Turn screen page :param state: button state """ filelist = Page(self.episodes, MENU_ROWS_EPISODES, MENU_COLUMNS_EPISODES) if state == None: filelist.current_page_index = self.current_page - 1 index = filelist.current_page_index * PAGE_SIZE_EPISODES else: if getattr(state, "status", None) == STATUS_LOADED: if hasattr(state, "original_url") and len( state.original_url.strip()) > 0: filelist.set_current_item_by_url(state.original_url) else: filelist.set_current_item_by_file_name(state.file_name) else: filelist.set_current_item_by_url(state.url) index = filelist.current_item_index self.current_page = filelist.current_page_index + 1 page = filelist.get_current_page() d = self.episodes_menu.make_dict(page) self.episodes_menu.set_items(d, filelist.current_page_index, self.select_episode, False) self.set_title(self.current_page) self.episodes_menu.unselect() self.episodes_menu.select_by_index(index) for b in self.episodes_menu.buttons.values(): b.parent_screen = self self.navigator.left_button.change_label( str(filelist.get_left_items_number())) self.navigator.right_button.change_label( str(filelist.get_right_items_number())) self.episodes_menu.clean_draw_update() if hasattr(self, "update_observer"): self.episodes_menu.add_menu_observers(self.update_observer, self.redraw_observer) def select_episode(self, state): """ Select podacst episode :param state: button state """ if getattr(state, "long_press", None) == True: if state.status == STATUS_LOADED: self.podcasts_util.delete_episode(state) if not self.util.connected_to_internet: for i, c in enumerate(self.episodes): if c.name == state.name: del self.episodes[i] break if len(self.episodes) == 0: self.title = " " self.set_title(0) self.turn_page(state) else: state.icon_base = state.event_origin.components[ 1].content = self.podcasts_util.available_icon state.status = STATUS_AVAILABLE elif state.status == STATUS_AVAILABLE: if self.podcasts_util.is_podcast_folder_available(): state.icon_base = state.event_origin.components[ 1].content = self.podcasts_util.loading_icon state.status = STATUS_LOADING self.add_save_episode_listener(state) self.podcasts_util.save_episode( state, self.notify_save_episode_listeners) self.clean_draw_update() else: podcast_player = self.listeners[KEY_PLAYER] podcast_player(state) def add_save_episode_listener(self, listener): """ Add save episode listener :param listener: event listener """ if listener not in self.save_episode_listeners: self.save_episode_listeners.append(listener) def notify_save_episode_listeners(self): """ Notify all save episode listeners """ for index, listener in enumerate(self.save_episode_listeners): listener.icon_base = listener.event_origin.components[ 1].content = self.podcasts_util.loaded_icon listener.status = STATUS_LOADED self.clean_draw_update() if hasattr(self, "redraw_observer"): self.redraw_observer() del self.save_episode_listeners[index] def add_screen_observers(self, update_observer, redraw_observer): """ Add screen observers :param update_observer: observer for updating the screen :param redraw_observer: observer to redraw the whole screen """ MenuScreen.add_screen_observers(self, update_observer, redraw_observer) self.update_observer = update_observer self.redraw_observer = redraw_observer self.add_loading_listener(redraw_observer) self.navigator.add_observers(self.update_observer, self.redraw_observer)
def __init__(self, util, listeners, voice_assistant, timer_lock, start_timer_thread): """ Initializer :param util: utility object :param listeners: screen navigator listeners :param voice_assistant: voice assistant :param timer_lock: lock object :param start_timer_thread: start timer thread function """ self.util = util self.timer_lock = timer_lock self.start_timer_thread = start_timer_thread self.config = util.config self.screen_layout = BorderLayout(util.screen_rect) self.screen_layout.set_percent_constraints(PERCENT_TITLE, PERCENT_NAV_HEIGHT, 0, 0) Screen.__init__(self, util, "", PERCENT_NAV_HEIGHT, voice_assistant, "timer_title", title_layout=self.screen_layout.TOP) self.bounding_box = util.screen_rect label = self.config[LABELS][TIMER] self.screen_title.set_text(label) try: self.config[TIMER] except: self.config[TIMER] = {} c = GridLayout(self.screen_layout.CENTER) c.set_pixel_constraints(2, 1) layout = BorderLayout(c.get_next_constraints()) layout.set_percent_constraints(0, 0, PERCENT_CLOCK, 100 - PERCENT_CLOCK) gap = layout.h * 0.1 layout.LEFT.w -= gap * 3 layout.LEFT.h -= gap digits = util.image_util.get_flipclock_digits(layout.LEFT) change_codes = [ HOURS_INCREMENT_SLEEP, MINUTES_INCREMENT_SLEEP, HOURS_DECREMENT_SLEEP, MINUTES_DECREMENT_SLEEP ] self.sleep_menu = SleepMenu(util, layout, gap, digits, self.handle_button, timer_lock, self.sleep_change_callback, change_codes) self.active_key_menu = self.sleep_menu self.add_component(self.sleep_menu) self.menu_index = -1 self.sleep_menu.clock.add_change_listener(self.handle_clock_change) layout = BorderLayout(c.get_next_constraints()) layout.set_percent_constraints(0, 0, PERCENT_CLOCK, 100 - PERCENT_CLOCK) change_codes = [ HOURS_INCREMENT_WAKE_UP, MINUTES_INCREMENT_WAKE_UP, HOURS_DECREMENT_WAKE_UP, MINUTES_DECREMENT_WAKE_UP ] self.wake_up_menu = WakeUpMenu(util, layout, gap, digits, self.handle_button, timer_lock, self.wake_up_change_callback, change_codes) self.add_component(self.wake_up_menu) self.wake_up_menu.clock.add_change_listener(self.handle_clock_change) self.navigator = TimerNavigator(self.util, self.screen_layout.BOTTOM, listeners) self.navigator.components[0].set_selected(True) self.add_component(self.navigator) self.home_button = self.navigator.get_button_by_name(KEY_HOME) self.sleep_now_button = self.navigator.get_button_by_name(SLEEP_NOW) self.player_button = self.navigator.get_button_by_name(KEY_PLAYER) self.menu_functions = { HOURS_INCREMENT_SLEEP: self.sleep_menu.clock.increment_hours, MINUTES_INCREMENT_SLEEP: self.sleep_menu.clock.increment_minutes, HOURS_DECREMENT_SLEEP: self.sleep_menu.clock.decrement_hours, MINUTES_DECREMENT_SLEEP: self.sleep_menu.clock.decrement_minutes, HOURS_INCREMENT_WAKE_UP: self.wake_up_menu.clock.increment_hours, MINUTES_INCREMENT_WAKE_UP: self.wake_up_menu.clock.increment_minutes, HOURS_DECREMENT_WAKE_UP: self.wake_up_menu.clock.decrement_hours, MINUTES_DECREMENT_WAKE_UP: self.wake_up_menu.clock.decrement_minutes } self.current_button = None self.link_borders() self.clean_draw_update()
def __init__(self, f, id, util, name, bgr, slider_color, img_knob, img_knob_on, key_incr, key_decr, key_knob, bb, listener, label): """ Initializer :param id: band ID :param util: utility object :param name: slider name :param bgr: slider background color :param slider_color: slider center line color :param img_knob: knob image :param img_knob_on: knob image in on state :param key_incr: keyboard key associated with slider increment action :param key_decr: keyboard key associated with slider decrement action :param key_knob: keyboard key associated with single click on knob :param bb: slider bounding box """ Container.__init__(self, util, background=bgr, bounding_box=bb, content=None) self.util = util self.config = util.config self.bgr = bgr self.VALUE_LAYER = 2 self.LABEL_LAYER = 1 layout = BorderLayout(bb) layout.set_percent_constraints(10.0, 10.0, 0.0, 0.0) self.value_name = name + ".value." + str(id) self.label_name = name + ".label." + str(id) self.value_layout = layout.TOP self.label_layout = layout.BOTTOM self.label_layout.y -= 1 self.label_layout.h += 2 self.slider = Slider(util, "slider." + str(id), bgr, slider_color, img_knob, img_knob_on, None, key_incr, key_decr, key_knob, layout.CENTER) self.slider.add_slide_listener(listener) self.add_component(self.slider) height = 60 font_size = int((self.value_layout.h * height) / 100.0) c = self.config[COLORS][COLOR_BRIGHT] self.top = f.create_output_text("top.label." + str(id), self.value_layout, bgr, c, font_size) self.bottom = f.create_output_text("bottom.label." + str(id), self.label_layout, bgr, c, font_size) self.bottom.set_text(label) self.add_component(self.top) self.add_component(self.bottom) self.seek_listeners = [] self.update_seek_listeners = True self.use_web = self.config[USAGE][USE_WEB]
class EqualizerMenu(Container): """ Equalizer Menu class """ def __init__(self, util, handle_slider_event, bounding_box=None): """ Initializer :param util: utility object :param handle_slider_event: slider event handler :param listeners: menu listeners :param bgr: menu background :param bounding_box: bounding box """ self.labels = [ "31", "63", "125", "250", "500", "1k", "2k", "4k", "8k", "16k" ] self.util = util Container.__init__(self, util) name = "equalizermenu" self.factory = Factory(util) self.bounding_box = bounding_box self.bounding_box.y += 1 self.bounding_box.h -= 1 self.bgr_color = util.config[BACKGROUND][MENU_BGR_COLOR] self.eq_layout = BorderLayout(self.bounding_box) self.eq_layout.set_percent_constraints(0, 0, 5, 5) self.bands = 10 self.sliders = self.add_sliders(handle_slider_event) self.current_slider = -1 self.left_filler = Component(util, self.eq_layout.LEFT) self.left_filler.name = name + ".bgr.left" self.left_filler.bgr = self.bgr_color self.add_component(self.left_filler) self.right_filler = Component(util, self.eq_layout.RIGHT) self.right_filler.name = name + ".bgr.right" self.right_filler.bgr = self.bgr_color self.add_component(self.right_filler) self.SLOW_INCREMENT = 1 self.FAST_INCREMENT = self.sliders[0].slider.knob_height / 2 self.mouse_events = [ pygame.MOUSEBUTTONUP, pygame.MOUSEBUTTONDOWN, pygame.MOUSEMOTION ] def add_sliders(self, handle_slider_event): layout = GridLayout(self.eq_layout.CENTER) layout.set_pixel_constraints(1, self.bands, 0, 0) layout.current_constraints = 0 sliders = [] for n in range(self.bands): constr = layout.get_next_constraints() s = self.factory.create_equalizer_slider(n, constr, "band", handle_slider_event, self.labels[n], self.bgr_color) s.slider.active = False s.content = None s.slider.content = None self.add_component(s) sliders.append(s) return sliders def set_bands(self, values): for n, s in enumerate(self.sliders): v = values[n] s.slider.set_position(v) s.slider.update_position() s.set_value(str(v)) s.slider.set_knob_off() self.current_slider = -1 def set_parent_screen(self, scr): """ Add parent screen :param scr: parent screen """ self.left_filler.parent_screen = scr self.right_filler.parent_screen = scr for s in self.sliders: s.slider.parent_screen = scr s.top.parent_screen = scr s.bottom.parent_screen = scr def handle_event(self, event): """ Menu event handler :param event: menu event """ if not self.visible: return if event.type == USER_EVENT_TYPE and event.sub_type == SUB_TYPE_KEYBOARD: key_events = [ kbd_keys[KEY_LEFT], kbd_keys[KEY_RIGHT], kbd_keys[KEY_UP], kbd_keys[KEY_DOWN], kbd_keys[KEY_PAGE_UP], kbd_keys[KEY_PAGE_DOWN], kbd_keys[KEY_SELECT] ] if event.keyboard_key not in key_events: return if not self.is_menu_selected(): return if event.action == pygame.KEYUP: self.key_up(event) elif event.action == pygame.KEYDOWN: self.key_down(event) elif event.type == SELECT_EVENT_TYPE: if event.source == self: return self.handle_select_action(event.x, event.y) elif event.type in self.mouse_events: if not self.bounding_box.collidepoint(event.pos): return Container.handle_event(self, event) def handle_select_action(self, x, y): """ Handle select action :param x: x coordinate :param y: y coordinate """ for index, s in enumerate(self.sliders): if s.bounding_box.collidepoint((x - 3, y)): s.slider.selected = True self.current_slider = index self.activate_slider() def key_up(self, event): """ Handle key up :param event: the event to handle """ avoided_keys = [ kbd_keys[KEY_UP], kbd_keys[KEY_DOWN], kbd_keys[KEY_PAGE_UP], kbd_keys[KEY_PAGE_DOWN] ] if self.current_slider == -1 and (event.keyboard_key in avoided_keys): return self.deactivate_current_slider() if event.keyboard_key == kbd_keys[KEY_LEFT]: if self.current_slider == 0 or self.current_slider == -1: y = self.exit_bottom_y slider = self.sliders[len(self.sliders) - 1].slider if self.exit_menu(y, event, slider): return else: self.current_slider -= 1 elif event.keyboard_key == kbd_keys[KEY_RIGHT]: if self.current_slider == len(self.sliders) - 1: y = self.exit_bottom_y slider = self.sliders[0].slider if self.exit_menu(y, event, slider): return else: self.current_slider += 1 elif event.keyboard_key == kbd_keys[KEY_SELECT]: if not self.is_menu_selected(): return slider = self.sliders[self.current_slider].slider if self.exit_menu(self.exit_bottom_y, event, slider): return self.activate_slider() def exit_menu(self, exit_border, event, slider): """ Exit menu :param exit_border: exit border :param event: exit event :param slider: the slider """ if exit_border == None or slider == None or event.action != pygame.KEYUP: return False x = int(slider.bounding_box.x + (slider.bounding_box.w / 2)) y = exit_border slider.selected = False self.current_slider = -1 for s in self.sliders: s.slider.selected = False self.util.post_exit_event(x, y, self) if self.redraw_observer: self.redraw_observer() return True def key_down(self, event): """ Handle key down :param event: the event to handle """ if self.current_slider == -1: return slider = self.sliders[self.current_slider].slider if event.keyboard_key == kbd_keys[KEY_UP]: slider.release_action( (slider.knob_x, slider.last_knob_position + slider.knob_height / 2 - self.SLOW_INCREMENT)) elif event.keyboard_key == kbd_keys[ KEY_DOWN] and event.action == pygame.KEYDOWN: slider.release_action( (slider.knob_x, slider.last_knob_position + slider.knob_height / 2 + self.SLOW_INCREMENT)) elif event.keyboard_key == kbd_keys[ KEY_PAGE_UP] and event.action == pygame.KEYDOWN: slider.release_action( (slider.knob_x, slider.last_knob_position - self.FAST_INCREMENT)) elif event.keyboard_key == kbd_keys[ KEY_PAGE_DOWN] and event.action == pygame.KEYDOWN: slider.release_action( (slider.knob_x, slider.last_knob_position + slider.knob_height / 2 + self.FAST_INCREMENT)) slider.set_knob_on() def deactivate_current_slider(self): """ Deactivate the current slider """ if self.current_slider == -1: return slider = self.sliders[self.current_slider].slider slider.release_action((slider.knob_x, slider.last_knob_position)) slider.clean_draw_update() def activate_slider(self): """ Activate the current slider """ slider = self.sliders[self.current_slider].slider slider.press_action() slider.clean_draw_update() def is_menu_selected(self): """ Check if menu has selected slider :return: True - has selected slider, False - doesn't have """ for s in self.sliders: if s.slider.selected: return True return False def add_menu_observers(self, update_observer, redraw_observer): """ Add menu observer :param update_observer: observer for updating menu :param redraw_observer: observer to redraw the whole screen """ self.redraw_observer = redraw_observer for s in self.sliders: s.slider.add_slide_listener(update_observer) s.slider.add_knob_listener(update_observer) s.slider.add_press_listener(update_observer) s.slider.add_motion_listener(update_observer) s.web_seek_listener = update_observer
def __init__(self, listeners, util, voice_assistant, screen_mode=STATION): """ Initializer :param util: utility object :param listener: screen menu event listener """ self.util = util self.config = util.config self.factory = Factory(util) self.screen_mode = screen_mode self.bounding_box = util.screen_rect self.favorites_util = FavoritesUtil(self.util) self.top_height = self.config[PLAYER_SCREEN][TOP_HEIGHT_PERCENT] self.bottom_height = self.config[PLAYER_SCREEN][BOTTOM_HEIGHT_PERCENT] self.button_height = self.config[PLAYER_SCREEN][BUTTON_HEIGHT_PERCENT] self.popup_width = self.config[PLAYER_SCREEN][POPUP_WIDTH_PERCENT] self.image_location = self.config[PLAYER_SCREEN][IMAGE_LOCATION] self.layout = self.get_layout() Screen.__init__(self, util, "", self.top_height, voice_assistant, "station_screen_title", True, self.layout.TOP) self.layout = self.get_layout() folders = self.util.get_stations_folders() if folders: panel_layout = self.get_panel_layout(self.layout, LOCATION_RIGHT) panel_layout.set_percent_constraints(self.button_height, self.button_height, 0, 0) self.genres = util.load_stations_folders(panel_layout.BOTTOM) self.genres[ KEY_FAVORITES] = self.favorites_util.get_favorites_button_state( panel_layout.BOTTOM) current_genre_name = list(self.genres.keys())[0] self.current_genre = self.genres[current_genre_name] self.items_per_line = self.items_per_line(self.layout.CENTER.w) items = [] if self.screen_mode == STATION: k = STATIONS + "." + self.config[CURRENT][LANGUAGE] try: self.config[k] self.current_genre = self.genres[self.config[k] [CURRENT_STATIONS]] except: self.config[k] = {} self.config[k][CURRENT_STATIONS] = self.current_genre.name items = self.load_stations( self.config[CURRENT][LANGUAGE], self.current_genre.name, self.items_per_line * self.items_per_line) elif self.screen_mode == STREAM: items = util.load_streams(self.items_per_line * self.items_per_line) self.playlist = Page(items, self.items_per_line, self.items_per_line) self.station_menu = StationMenu( self.playlist, util, screen_mode, self.config[BACKGROUND][SCREEN_BGR_COLOR], self.layout.CENTER) if self.station_menu.is_button_defined(): d = {"current_title": self.station_menu.button.state.l_name} self.screen_title.set_text(d) Container.add_component(self, self.station_menu) self.stop_player = listeners[KEY_STOP] self.create_left_panel(self.layout, listeners) self.create_right_panel(self.layout, listeners) self.home_button.add_release_listener(listeners[KEY_HOME]) if self.screen_mode == STATION: self.genres_button.add_release_listener(listeners[KEY_GENRES]) self.shutdown_button.add_release_listener( self.favorites_util.save_favorites) self.shutdown_button.add_release_listener(listeners[KEY_SHUTDOWN]) self.left_button.add_release_listener( self.station_menu.switch_to_previous_station) self.left_button.add_release_listener(self.update_arrow_button_labels) self.page_down_button.add_release_listener( self.station_menu.switch_to_previous_page) self.page_down_button.add_release_listener( self.update_arrow_button_labels) self.right_button.add_release_listener( self.station_menu.switch_to_next_station) self.right_button.add_release_listener(self.update_arrow_button_labels) self.page_up_button.add_release_listener( self.station_menu.switch_to_next_page) self.page_up_button.add_release_listener( self.update_arrow_button_labels) self.station_menu.add_listener(listeners[KEY_PLAY]) self.station_menu.add_listener(self.screen_title.set_state) self.station_menu.add_listener(self.update_arrow_button_labels) self.station_menu.add_mode_listener(self.mode_listener) self.info_button = None self.info_popup = None self.start_screensaver = listeners[SCREENSAVER] bottom_layout = BorderLayout(self.layout.BOTTOM) bottom_layout.set_percent_constraints(0, 0, 0, self.popup_width) volume_layout = bottom_layout.CENTER volume_layout.w -= 2 volume_layout.x += 1 self.volume = self.factory.create_volume_control(volume_layout) self.volume.add_slide_listener(listeners[KEY_SET_VOLUME]) self.volume.add_slide_listener(listeners[KEY_SET_CONFIG_VOLUME]) self.volume.add_slide_listener(listeners[KEY_SET_SAVER_VOLUME]) self.volume.add_knob_listener(listeners[KEY_MUTE]) self.add_component(self.volume) self.player_screen = True self.add_popup(bottom_layout.RIGHT) if self.current_genre.name == KEY_FAVORITES: self.favorites_mode = True else: self.favorites_mode = False self.favorites_util.set_favorites_in_config(self.items_per_line) self.animated_title = True
def __init__(self, util, bounding_box, listeners, pages): """ Initializer :param util: utility object :param bounding_box: bounding box :param listeners: buttons listeners :param pages: number of Wi-Fi menu pages """ Container.__init__(self, util) self.factory = Factory(util) self.name = "wifi.navigator" self.content = bounding_box self.content_x = bounding_box.x self.content_y = bounding_box.y self.menu_buttons = [] bgr = util.config[COLORS][COLOR_DARK_LIGHT] arrow_layout = BorderLayout(bounding_box) arrow_layout.set_percent_constraints(0, 0, PERCENT_ARROW_WIDTH, PERCENT_ARROW_WIDTH) if pages > 1: constr = arrow_layout.LEFT self.left_button = self.factory.create_page_down_button( constr, "0", 40, 100) self.left_button.add_release_listener(listeners[GO_LEFT_PAGE]) self.add_component(self.left_button) self.menu_buttons.append(self.left_button) constr = arrow_layout.RIGHT self.right_button = self.factory.create_page_up_button( constr, "0", 40, 100) self.right_button.add_release_listener(listeners[GO_RIGHT_PAGE]) self.add_component(self.right_button) self.menu_buttons.append(self.right_button) layout = GridLayout(arrow_layout.CENTER) else: layout = GridLayout(bounding_box) layout.set_pixel_constraints(1, 5, 1, 0) layout.current_constraints = 0 image_size = 64 constr = layout.get_next_constraints() self.home_button = self.factory.create_button( KEY_HOME, KEY_HOME, constr, listeners[KEY_HOME], bgr, image_size_percent=image_size) self.add_component(self.home_button) self.menu_buttons.append(self.home_button) constr = layout.get_next_constraints() self.sort_strength = self.factory.create_button( KEY_REFRESH, KEY_SETUP, constr, listeners[KEY_REFRESH], bgr, image_size_percent=image_size) self.add_component(self.sort_strength) self.menu_buttons.append(self.sort_strength) constr = layout.get_next_constraints() self.sort_abc = self.factory.create_button( KEY_SORT, KEY_PARENT, constr, listeners[KEY_SORT], bgr, image_size_percent=image_size) self.add_component(self.sort_abc) self.menu_buttons.append(self.sort_abc) constr = layout.get_next_constraints() self.network_button = self.factory.create_button( KEY_NETWORK, KEY_BACK, constr, listeners[KEY_CALLBACK], bgr, image_size_percent=image_size) self.add_component(self.network_button) self.menu_buttons.append(self.network_button) constr = layout.get_next_constraints() self.player_button = self.factory.create_button( KEY_PLAYER, KEY_PLAY_PAUSE, constr, listeners[KEY_PLAYER], bgr, image_size_percent=image_size) self.add_component(self.player_button) self.menu_buttons.append(self.player_button)
def __init__(self, util, listeners, voice_assistant): """ Initializer :param util: utility object :param listeners: listeners :param voice_assistant: voice assistant """ self.util = util self.config = util.config self.listeners = listeners self.factory = Factory(util) self.go_home = listeners[KEY_HOME] self.go_file_playback = listeners[KEY_PLAY_COLLECTION] dbutil = util.get_db_util() self.selector = Selector(dbutil) self.bounding_box = util.screen_rect layout = BorderLayout(self.bounding_box) layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, 0, 0) MenuScreen.__init__(self, util, listeners, ROWS, COLUMNS, voice_assistant, [ROWS, COLUMNS], self.turn_page, page_in_title=False, show_loading=False) self.navigator = TopicDetailNavigator(self.util, self.layout.BOTTOM, listeners) self.add_navigator(self.navigator) self.left_button = self.navigator.get_button_by_name(KEY_PAGE_DOWN) self.right_button = self.navigator.get_button_by_name(KEY_PAGE_UP) self.player_button = self.navigator.get_button_by_name(KEY_PLAYER) m = self.factory.create_collection_menu_button font_size = int(((self.menu_layout.h / ROWS) / 100) * FONT_HEIGHT) h = self.config[HORIZONTAL_LAYOUT] bgr = self.config[BACKGROUND][MENU_BGR_COLOR] self.collection_list_menu = Menu(util, bgr, self.menu_layout, ROWS, COLUMNS, create_item_method=m, align=ALIGN_LEFT, horizontal_layout=h, font_size=font_size) self.set_menu(self.collection_list_menu) self.current_item = None self.current_page_items = None self.first_item = None self.last_item = None self.collection_topic = None self.selection = None self.prev_page = 1 self.animated_title = True
class Screen(Container): """ Base class for all screens. Extends Container class """ def __init__(self, util, title_key, percent_bottom_height=0, voice_assistant=None, screen_title_name="screen_title", create_dynamic_title=False, title_layout=None, title=None): """ Initializer :param util: utility object :param title_key: the resource bundle key for the screen title """ Container.__init__(self, util) self.util = util factory = Factory(util) self.config = util.config self.bounding_box = self.config[SCREEN_RECT] self.bgr = (0, 0, 0) self.layout = BorderLayout(self.config[SCREEN_RECT]) self.layout.set_percent_constraints(PERCENT_TOP_HEIGHT, percent_bottom_height, 0, 0) self.voice_assistant = voice_assistant self.font_size = int((self.layout.TOP.h * PERCENT_TITLE_FONT) / 100.0) d = self.config[COLORS][COLOR_DARK_LIGHT] c = self.config[COLORS][COLOR_CONTRAST] t_layout = self.layout.TOP if title_layout: t_layout = title_layout if create_dynamic_title: self.screen_title = factory.create_dynamic_text( screen_title_name, t_layout, d, c, self.font_size) else: self.screen_title = factory.create_output_text( screen_title_name, t_layout, d, c, self.font_size) if title: self.screen_title.set_text(title) else: if title_key and len(title_key) > 0: try: label = self.config[LABELS][title_key] self.screen_title.set_text(label) except: pass if voice_assistant: self.screen_title.add_select_listener(self.handle_voice_assistant) self.layout.TOP.w += 1 self.voice_command = factory.create_output_text( "voice_command", self.layout.TOP, d, c, self.font_size) self.voice_command.add_select_listener(self.handle_voice_assistant) self.voice_assistant.add_text_recognized_listener( self.text_recognized) self.voice_assistant.assistant.add_start_conversation_listener( self.start_conversation) self.voice_assistant.assistant.add_stop_conversation_listener( self.stop_conversation) if voice_assistant and voice_assistant.is_running(): self.title_selected = False else: self.title_selected = True self.draw_title_bar() self.player_screen = False self.update_web_observer = None self.update_web_title = None def draw_title_bar(self): """ Draw title bar on top of the screen """ if len(self.components) != 0 and self.title_selected: self.components[0] = self.voice_command self.title_selected = False elif len(self.components) != 0 and not self.title_selected: self.components[0] = self.screen_title self.title_selected = True elif len(self.components) == 0 and self.title_selected: self.components.append(self.screen_title) elif len(self.components) == 0 and not self.title_selected: self.voice_command.set_text( self.config[LABELS][KEY_WAITING_FOR_COMMAND]) self.components.append(self.voice_command) def handle_voice_assistant(self, state=None): """ Start/Stop voice assistant :state: not used """ if self.title_selected: self.voice_assistant.start() else: self.voice_assistant.stop() def text_recognized(self, text): """ Handle text recognized event :text: recognized text """ c = self.config[LABELS][KEY_VA_COMMAND] + " " + text self.voice_command.set_text(c) self.voice_command.clean_draw_update() if self.update_web_title: self.update_web_title(self.voice_command) def start_conversation(self, event): """ Start voice conversation :event: not used """ if self.visible: self.voice_command.set_visible(True) self.voice_command.set_text_no_draw( self.config[LABELS][KEY_WAITING_FOR_COMMAND]) self.components[0] = self.voice_command self.title_selected = False if self.visible: self.voice_command.clean_draw_update() if self.update_web_observer: self.update_web_observer() def stop_conversation(self): """ Stop voice conversation """ if self.visible: self.screen_title.set_visible(True) self.components[0] = self.screen_title self.title_selected = True if self.visible: self.screen_title.clean_draw_update() if self.update_web_observer: self.update_web_observer() def add_menu(self, menu): """ Add menu to the screen :param menu: the menu to add """ self.menu = menu self.add_component(menu) def enable_player_screen(self, flag): """ Enable player screen :param flag: enable/disable flag """ pass def exit_screen(self): """ Complete actions required to save screen state """ self.set_visible(False) def add_screen_observers(self, update_observer, redraw_observer, title_to_json=None): """ Add screen observers. :param update_observer: observer for updating the screen :param redraw_observer: observer to redraw the whole screen """ self.update_web_observer = redraw_observer self.update_web_title = title_to_json
def __init__(self, util, listeners, voice_assistant): """ Initializer :param util: utility object :param listeners: listeners :param voice_assistant: voice assistant """ self.util = util self.config = util.config self.listeners = listeners self.factory = Factory(util) self.go_home = listeners[KEY_HOME] self.go_keyboard = listeners[KEY_KEYBOARD_KEY] self.keyboard_callback = listeners[KEY_CALLBACK] self.wifi_selection_listeners = [] self.wifi_util = WiFiUtil(util) self.bounding_box = util.screen_rect layout = BorderLayout(self.bounding_box) layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, 0, 0) d = [MENU_ROWS_WIFI, MENU_COLUMNS_WIFI] MenuScreen.__init__(self, util, listeners, MENU_ROWS_WIFI, MENU_COLUMNS_WIFI, voice_assistant, d, self.turn_page, page_in_title=False, show_loading=False) self.title = self.config[LABELS]["select.wifi"] self.set_title(1) listeners[KEY_REFRESH] = self.set_current listeners[KEY_SORT] = self.sort_abc self.navigator = WiFiNavigator(self.util, self.layout.BOTTOM, listeners, PAGE_SIZE_WIFI + 1) self.add_component(self.navigator) self.network_button = self.navigator.get_button_by_name(KEY_NETWORK) m = self.factory.create_wifi_menu_button font_height = 32 font_size = int( ((self.menu_layout.h / MENU_ROWS_WIFI) / 100) * font_height) self.wifi_menu = MultiPageMenu(util, self.next_page, self.previous_page, self.set_title, self.reset_title, self.go_to_page, m, MENU_ROWS_WIFI, MENU_COLUMNS_WIFI, None, (0, 0, 0), self.menu_layout, align=ALIGN_CENTER, font_size=font_size) self.set_menu(self.wifi_menu) self.original_networks = None self.networks = None self.sort_direction = False self.current_network = None self.link_borders()
class FilePlayerScreen(Screen): """ File Player Screen """ def __init__(self, listeners, util, get_current_playlist, voice_assistant, player_stop=None): """ Initializer :param listeners: screen listeners :param util: utility object """ self.util = util self.config = util.config self.cdutil = CdUtil(util) self.factory = Factory(util) self.stop_player = player_stop self.get_current_playlist = get_current_playlist self.bounding_box = self.config[SCREEN_RECT] self.layout = BorderLayout(self.bounding_box) k = self.bounding_box.w / self.bounding_box.h percent_menu_width = (100.0 - PERCENT_TOP_HEIGHT - PERCENT_BOTTOM_HEIGHT) / k panel_width = (100.0 - percent_menu_width) / 2.0 self.layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, panel_width, panel_width) self.voice_assistant = voice_assistant Screen.__init__(self, util, "", PERCENT_TOP_HEIGHT, voice_assistant, "file_player_screen_title", True, self.layout.TOP) self.layout = BorderLayout(self.bounding_box) self.layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, panel_width, panel_width) self.create_left_panel(self.layout, listeners) self.create_right_panel(self.layout, listeners) self.file_button = self.factory.create_file_button( self.layout.CENTER, listeners[AUDIO_FILES]) Container.add_component(self, self.file_button) self.audio_files = self.get_audio_files() self.home_button.add_release_listener(listeners[KEY_HOME]) self.shutdown_button.add_release_listener(listeners[KEY_SHUTDOWN]) self.left_button.add_release_listener(self.go_left) self.right_button.add_release_listener(self.go_right) self.volume = self.factory.create_volume_control(self.layout.BOTTOM) self.volume.add_slide_listener(listeners[KEY_SET_VOLUME]) self.volume.add_slide_listener(listeners[KEY_SET_CONFIG_VOLUME]) self.volume.add_slide_listener(listeners[KEY_SET_SAVER_VOLUME]) self.volume.add_knob_listener(listeners[KEY_MUTE]) self.volume_visible = False self.volume.set_visible(self.volume_visible) Container.add_component(self, self.volume) self.time_control = self.factory.create_time_control( self.layout.BOTTOM) self.time_control.add_seek_listener(listeners[KEY_SEEK]) self.play_button.add_listener(PAUSE, self.time_control.pause) self.play_button.add_listener(KEY_PLAY, self.time_control.resume) self.left_button.add_release_listener( self.play_button.draw_default_state) self.right_button.add_release_listener( self.play_button.draw_default_state) if self.config[PLAYER_SETTINGS][PAUSE]: self.time_control.pause() Container.add_component(self, self.time_control) self.play_listeners = [] self.add_play_listener(listeners[KEY_PLAY]) self.current_folder = self.config[FILE_PLAYBACK][CURRENT_FOLDER] self.file_button.state.cover_art_folder = self.util.file_util.get_cover_art_folder( self.current_folder) self.playlist_size = 0 self.player_screen = True self.cd_album = None def get_audio_files(self): """ Return the list of audio files in current folder :return: list of audio files """ files = [] if self.config[CURRENT][MODE] == CD_PLAYER: af = getattr(self, "audio_files", None) if af == None: cd_drive_name = self.config[CD_PLAYBACK][CD_DRIVE_NAME] files = self.cdutil.get_cd_tracks_summary(cd_drive_name) else: return self.audio_files else: folder = self.config[FILE_PLAYBACK][CURRENT_FOLDER] files = self.util.get_audio_files_in_folder(folder) return files def get_current_track_index(self, state=None): """ Return current track index. In case of files goes through the file list. In case of playlist takes track index from the state object. :return: current track index """ if self.config[CURRENT][MODE] == AUDIOBOOKS: t = None try: t = state["file_name"] except: pass if t == None: try: t = state["current_title"] except: pass if t == None: try: t = state except: pass for i, f in enumerate(self.audio_files): try: s = f["file_name"] except: pass if getattr(f, "file_name", None): s = getattr(f, "file_name", None) if s.endswith(t): return i return 0 mode = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] if state and mode == FILE_PLAYLIST: try: n = state["Track"] if n: return int(n) - 1 except: pass if self.config[CURRENT][MODE] == CD_PLAYER: cmp = int(self.config[CD_PLAYBACK][CD_TRACK]) - 1 else: cmp = self.config[FILE_PLAYBACK][CURRENT_FILE] if state and isinstance(state, State): cmp = state.file_name for f in self.audio_files: if self.config[CURRENT][MODE] == CD_PLAYER: if f.index == cmp: return f.index else: if f.file_name == cmp: return f.index return 0 def stop_recursive_playback(self): """ Stop recursive playback """ self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] = FILE_AUDIO self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST] = None self.config[FILE_PLAYBACK][CURRENT_TRACK_TIME] = None self.stop_timer() self.time_control.stop_thread() self.time_control.reset() if self.stop_player != None: self.stop_player() def go_left(self, state): """ Switch to the previous track :param state: not used state object """ if getattr(self, "current_track_index", None) == None: return filelist_size = self.get_filelist_size() if self.config[FILE_PLAYBACK][ CURRENT_FILE_PLAYBACK_MODE] == FILE_RECURSIVE: self.stop_recursive_playback() return if self.current_track_index == 0: self.current_track_index = filelist_size - 1 else: self.current_track_index -= 1 self.change_track(self.current_track_index) def go_right(self, state): """ Switch to the next track :param state: not used state object """ if getattr(self, "current_track_index", None) == None: return filelist_size = self.get_filelist_size() if self.config[FILE_PLAYBACK][ CURRENT_FILE_PLAYBACK_MODE] == FILE_RECURSIVE and self.current_track_index == filelist_size - 1: self.stop_timer() self.time_control.stop_thread() self.recursive_change_folder() self.current_track_index = 0 self.change_track(self.current_track_index) self.file_button.clean_draw_update() return if self.current_track_index == filelist_size - 1: self.current_track_index = 0 else: self.current_track_index += 1 self.change_track(self.current_track_index) def get_filelist_size(self): """ Return the file list size :return: file list size """ if self.audio_files: return len(self.audio_files) else: return 0 def change_track(self, track_index): """ Change track :param track_index: index track """ a = [AUDIOBOOKS, CD_PLAYER] m = self.config[CURRENT][MODE] if not (m in a): self.config[FILE_PLAYBACK][CURRENT_FILE] = self.get_filename( track_index) self.stop_timer() time.sleep(0.3) s = State() if m == FILE_PLAYBACK: s.playback_mode = self.config[FILE_PLAYBACK][ CURRENT_FILE_PLAYBACK_MODE] s.playlist_track_number = track_index s.index = track_index s.source = ARROW_BUTTON s.file_name = self.get_filename(track_index) if self.cd_album != None: s.album = self.cd_album self.set_current(True, s) def stop_timer(self): """ Stop time control timer """ self.time_control.stop_timer() def get_filename(self, index): """ Get filename by index :param index: file index :return: filename """ for b in self.audio_files: if b.index == index: return b.file_name return "" def is_valid_mode(self): current_mode = self.config[CURRENT][MODE] modes = [AUDIO_FILES, AUDIOBOOKS, CD_PLAYER] if current_mode in modes: return True else: return False def update_arrow_button_labels(self, state=None): """ Update left/right arrow button labels :param state: state object representing current track """ if (not self.is_valid_mode()) or (not self.screen_title.active): return self.set_current_track_index(state) left = self.current_track_index right = 0 if self.audio_files and len(self.audio_files) > 1: right = len(self.audio_files) - self.current_track_index - 1 self.left_button.change_label(str(left)) self.right_button.change_label(str(right)) def set_current_track_index(self, state): """ Set current track index :param state: state object representing current track """ if not self.is_valid_mode(): return if self.config[CURRENT][MODE] == CD_PLAYER and getattr( state, "cd_track_id", None): self.config[CD_PLAYBACK][CD_TRACK] = state["cd_track_id"] self.current_track_index = 0 if self.playlist_size == 1: return if not self.audio_files: self.audio_files = self.get_audio_files() if not self.audio_files: return i = self.get_current_track_index(state) self.current_track_index = i def create_left_panel(self, layout, listeners): """ Create left side buttons panel :param layout: panel layout :param listeners: button listeners """ panel_layout = BorderLayout(layout.LEFT) panel_layout.set_percent_constraints(PERCENT_SIDE_BOTTOM_HEIGHT, PERCENT_SIDE_BOTTOM_HEIGHT, 0, 0) self.left_button = self.factory.create_left_button( panel_layout.CENTER, '', 40, 100) self.shutdown_button = self.factory.create_shutdown_button( panel_layout.TOP) self.home_button = self.factory.create_button(KEY_HOME, KEY_HOME, panel_layout.BOTTOM, image_size_percent=36) panel = Container(self.util, layout.LEFT) panel.add_component(self.shutdown_button) panel.add_component(self.left_button) panel.add_component(self.home_button) Container.add_component(self, panel) def create_right_panel(self, layout, listeners): """ Create right side buttons panel :param layout: panel layout :param listeners: button listeners """ panel_layout = BorderLayout(layout.RIGHT) panel_layout.set_percent_constraints(PERCENT_SIDE_BOTTOM_HEIGHT, PERCENT_SIDE_BOTTOM_HEIGHT, 0, 0) self.play_button = self.factory.create_play_pause_button( panel_layout.TOP, listeners[KEY_PLAY_PAUSE]) self.right_button = self.factory.create_right_button( panel_layout.CENTER, '', 40, 100) self.time_volume_button = self.factory.create_time_volume_button( panel_layout.BOTTOM, self.toggle_time_volume) panel = Container(self.util, layout.RIGHT) panel.add_component(self.play_button) panel.add_component(self.right_button) panel.add_component(self.time_volume_button) Container.add_component(self, panel) def toggle_time_volume(self): """ Switch between time and volume controls """ if self.volume_visible: self.volume.set_visible(False) self.time_control.set_visible(True) self.volume_visible = False else: volume_level = int(self.config[PLAYER_SETTINGS][VOLUME]) self.volume.set_position(volume_level) self.volume.update_position() self.volume.set_visible(True) self.time_control.set_visible(False) self.volume_visible = True self.clean_draw_update() def eject_cd(self, state): """ Eject CD :param state: button state object """ self.audio_files = [] self.screen_title.set_text(" ") self.update_arrow_button_labels(state) self.time_control.reset() self.cd_album = None self.set_cd_album_art_image() def set_current(self, new_track=False, state=None): """ Set current file or playlist :param new_track: True - new audio file """ self.cd_album = getattr(state, "album", None) if self.config[CURRENT][MODE] == AUDIO_FILES: state.full_screen_image = self.set_audio_file_image() elif self.config[CURRENT][MODE] == CD_PLAYER and getattr( state, "source", None) != INIT: state.full_screen_image = self.set_cd_album_art_image() state.image_base = self.file_button.components[1].content config_volume_level = int(self.config[PLAYER_SETTINGS][VOLUME]) if state: state.volume = config_volume_level self.set_audio_file(new_track, state) if self.volume.get_position() != config_volume_level: self.volume.set_position(config_volume_level) self.volume.update_position() def set_audio_file_image(self): """ Set audio file image """ f = self.config[FILE_PLAYBACK][CURRENT_FOLDER] if not f: return None img_tuple = self.util.get_audio_file_icon(f, self.bounding_box) self.set_file_button(img_tuple) return img_tuple[1] def set_cd_album_art_image(self): """ Set CD album art image """ img_tuple = self.util.get_cd_album_art(self.cd_album, self.bounding_box) if img_tuple == None: return None self.set_file_button(img_tuple) self.file_button.clean_draw_update() return img_tuple[1] def set_file_button(self, img_tuple): """ Set image in file button :param img_tuple: tuple where first element is image location, second element image itself """ full_screen_image = img_tuple[1] self.file_button.state.full_screen_image = full_screen_image scale_ratio = self.util.get_scale_ratio( (self.layout.CENTER.w, self.layout.CENTER.h), full_screen_image) img = self.util.scale_image(full_screen_image, scale_ratio) self.file_button.components[1].content = img self.file_button.state.icon_base = img self.file_button.components[ 1].image_filename = self.file_button.state.image_filename = img_tuple[ 0] self.file_button.components[1].content_x = self.layout.CENTER.x + ( self.layout.CENTER.w - img.get_size()[0]) / 2 if self.layout.CENTER.h > img.get_size()[1]: self.file_button.components[ 1].content_y = self.layout.CENTER.y + int( (self.layout.CENTER.h - img.get_size()[1]) / 2) + 1 else: self.file_button.components[1].content_y = self.layout.CENTER.y def set_audio_file(self, new_track, s=None): """ Set new audio file :param new_track: True - new audio file "param s" button state object """ state = State() if s: state.playback_mode = getattr(s, "playback_mode", FILE_AUDIO) state.playlist_track_number = getattr(s, "playlist_track_number", None) if self.config[CURRENT][MODE] == CD_PLAYER and getattr( s, "source", None) != INIT: image_base = getattr(s, "image_base", None) if image_base != None: state.image_base = image_base else: m = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] if m: state.playback_mode = m else: state.playback_mode = FILE_AUDIO if self.config[CURRENT][MODE] == AUDIO_FILES: self.current_folder = self.config[FILE_PLAYBACK][CURRENT_FOLDER] if not self.current_folder: return state.folder = self.current_folder state.file_name = self.config[FILE_PLAYBACK][CURRENT_FILE] if state.folder[-1] == os.sep: state.folder = state.folder[:-1] if os.sep in state.file_name: state.url = "\"" + state.file_name + "\"" else: state.url = "\"" + state.folder + os.sep + state.file_name + "\"" state.music_folder = self.config[AUDIO][MUSIC_FOLDER] elif self.config[CURRENT][MODE] == CD_PLAYER: state.file_name = s.file_name state.url = getattr(s, "url", s.file_name) parts = s.file_name.split() self.config[CD_PLAYBACK][CD_DRIVE_NAME] = parts[0][len("cdda:///" ):] id = self.cdutil.get_cd_drive_id_by_name( self.config[CD_PLAYBACK][CD_DRIVE_NAME]) self.config[CD_PLAYBACK][CD_DRIVE_ID] = int(id) self.config[CD_PLAYBACK][CD_TRACK] = int(parts[1].split("=")[1]) state.mute = self.config[PLAYER_SETTINGS][MUTE] state.pause = self.config[PLAYER_SETTINGS][PAUSE] state.file_type = FILE_AUDIO state.dont_notify = True state.source = FILE_AUDIO if self.config[FILE_PLAYBACK][ CURRENT_FILE_PLAYBACK_MODE] == FILE_AUDIO or self.config[ CURRENT][MODE] == CD_PLAYER: self.audio_files = self.get_audio_files() elif self.config[FILE_PLAYBACK][ CURRENT_FILE_PLAYBACK_MODE] == FILE_PLAYLIST: state.file_name = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST] self.load_playlist(state) state.file_name = self.config[FILE_PLAYBACK][CURRENT_FILE] self.audio_files = self.get_audio_files_from_playlist() state.playback_mode = FILE_PLAYLIST n = getattr(s, "file_name", None) if n: state.file_name = n try: state.playlist_track_number = int(state.file_name) - 1 except: state.playlist_track_number = self.get_current_track_index( state) source = None if s: source = getattr(s, "source", None) if new_track: tt = 0.0 else: if self.config[CURRENT][MODE] == CD_PLAYER: tt = self.config[CD_PLAYBACK][CD_TRACK_TIME] else: tt = self.config[FILE_PLAYBACK][CURRENT_TRACK_TIME] if (isinstance(tt, str) and len(tt) != 0) or ( isinstance(tt, float) or (source and source == RESUME)) or isinstance(tt, int): state.track_time = tt self.time_control.slider.set_position(0) if self.file_button and self.file_button.components[ 1] and self.file_button.components[1].content: state.icon_base = self.file_button.components[1].content if s and s.volume: state.volume = s.volume if self.config[CURRENT][MODE] == CD_PLAYER and s and getattr( s, "source", None) == INIT: try: self.cd_album = self.util.cd_titles[self.config[CD_PLAYBACK] [CD_DRIVE_NAME]] self.set_cd_album_art_image() state.image_base = self.file_button.components[1].content except: self.cd_album = None if getattr(s, "full_screen_image", None) != None: state.full_screen_image = s.full_screen_image self.notify_play_listeners(state) def get_audio_files_from_playlist(self): """ Call player for files in the playlist :return: list of files from playlist """ playlist = self.get_current_playlist() files = [] if playlist: for n in range(len(playlist)): st = State() st.index = st.comparator_item = n st.file_type = FILE_AUDIO st.file_name = playlist[n] files.append(st) return files def restore_current_folder(self, state=None): """ Set current folder in config object :param state: not used """ self.config[FILE_PLAYBACK][CURRENT_FOLDER] = self.current_folder def set_audio_file_playlist(self, index): """ Set file in playlist :param index: file index in playlist """ state = State() state.playback_mode = FILE_PLAYLIST state.playlist_track_number = index state.file_type = FILE_AUDIO self.current_folder = self.config[FILE_PLAYBACK][CURRENT_FOLDER] self.notify_play_listeners(state) def go_back(self): """ Go back """ if self.config[CURRENT][MODE] == CD_PLAYER: return img_tuple = self.util.get_audio_file_icon(self.current_folder, self.layout.CENTER) img = img_tuple[1] self.file_button.components[1].content = img self.file_button.state.icon_base = img_tuple self.file_button.components[1].content_x = self.layout.CENTER.x if self.layout.CENTER.h > img.get_size()[1]: self.file_button.components[ 1].content_y = self.layout.CENTER.y + int( (self.layout.CENTER.h - img.get_size()[1]) / 2) else: self.file_button.components[1].content_y = self.layout.CENTER.y def recursive_change_folder(self): start_folder = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST] current_folder = self.config[FILE_PLAYBACK][CURRENT_FOLDER] f = self.util.file_util.get_next_folder_with_audio_files( start_folder, current_folder) if f == None or (f != None and f[0] == None): self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] = FILE_AUDIO self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST] = None return False self.config[FILE_PLAYBACK][CURRENT_FOLDER] = f[0] self.config[FILE_PLAYBACK][CURRENT_FILE] = f[1] self.config[FILE_PLAYBACK][CURRENT_TRACK_TIME] = None state = State() state.file_type = FOLDER state.url = f[0] state.long_press = True state.playback_mode = FILE_RECURSIVE self.current_track_index = 0 state.dont_notify = True self.audio_files = self.get_audio_files() self.recursive_notifier(f[0]) return True def end_of_track(self): """ Handle end of track """ if not self.screen_title.active: return i = getattr(self, "current_track_index", None) if i == None: return self.stop_timer() mode = self.config[CURRENT][MODE] if mode == RADIO or mode == STREAM or not self.audio_files: return self.time_control.stop_thread() if self.config[AUTO_PLAY_NEXT_TRACK]: if self.current_track_index == len(self.audio_files) - 1: if self.config[FILE_PLAYBACK][ CURRENT_FILE_PLAYBACK_MODE] == FILE_RECURSIVE: if not self.recursive_change_folder(): self.stop_recursive_playback() return elif self.config[CYCLIC_PLAYBACK]: self.current_track_index = 0 else: self.current_track_index += 1 if mode == AUDIO_FILES: self.config[FILE_PLAYBACK][CURRENT_TRACK_TIME] = None elif mode == AUDIOBOOKS: self.config[AUDIOBOOKS][BROWSER_BOOK_TIME] = None self.change_track(self.current_track_index) if self.config[FILE_PLAYBACK][ CURRENT_FILE_PLAYBACK_MODE] == FILE_RECURSIVE: self.file_button.clean_draw_update() def set_playlist_size(self, size): """ Set playlist size :param size: playlist size """ self.playlist_size = size self.stop_timer() def set_visible(self, flag): """ Set visibility flag :param flag: True - screen visible, False - screen invisible """ Container.set_visible(self, flag) if flag: if self.volume_visible: self.volume.set_visible(True) self.time_control.set_visible(False) else: self.volume.set_visible(False) self.time_control.set_visible(True) def add_play_listener(self, listener): """ Add play listener :param listener: event listener """ if listener not in self.play_listeners: self.play_listeners.append(listener) def notify_play_listeners(self, state): """ Notify all play listeners :param state: button state """ if not self.screen_title.active: return m = getattr(state, "playback_mode", None) if m != None and m != FILE_PLAYLIST: state.icon_base = self.file_button.state.icon_base folder = getattr(state, "folder", None) if folder: state.cover_art_folder = self.util.file_util.get_cover_art_folder( state.folder) for listener in self.play_listeners: listener(state) def enable_player_screen(self, flag): """ Enable player screen :param flag: enable/disable flag """ self.screen_title.active = flag self.time_control.active = flag def add_screen_observers(self, update_observer, redraw_observer, start_time_control, stop_time_control, title_to_json): """ Add screen observers :param update_observer: observer for updating the screen :param redraw_observer: observer to redraw the whole screen :param start_time_control: :param stop_time_control: :param title_to_json: """ Screen.add_screen_observers(self, update_observer, redraw_observer, title_to_json) self.add_button_observers(self.shutdown_button, update_observer, redraw_observer=None) self.shutdown_button.add_cancel_listener(redraw_observer) self.screen_title.add_listener(redraw_observer) self.add_button_observers(self.play_button, update_observer, redraw_observer=None) self.add_button_observers(self.home_button, update_observer, redraw_observer) self.add_button_observers(self.left_button, update_observer, redraw_observer=None) self.left_button.add_label_listener(update_observer) self.add_button_observers(self.right_button, update_observer, redraw_observer=None) self.right_button.add_label_listener(update_observer) self.volume.add_slide_listener(update_observer) self.volume.add_knob_listener(update_observer) self.volume.add_press_listener(update_observer) self.volume.add_motion_listener(update_observer) self.add_button_observers(self.time_volume_button, update_observer, redraw_observer, release=False) self.add_button_observers(self.file_button, update_observer, redraw_observer, press=False, release=False) self.time_control.web_seek_listener = update_observer self.time_control.add_start_timer_listener(start_time_control) self.time_control.add_stop_timer_listener(stop_time_control) self.time_control.slider.add_slide_listener(update_observer) self.time_control.slider.add_knob_listener(update_observer) self.time_control.slider.add_press_listener(update_observer) self.time_control.slider.add_motion_listener(update_observer)
def __init__(self, util, listeners, voice_assistant): """ Initializer :param util: utility object :param listeners: listeners :param voice_assistant: voice assistant """ self.util = util self.config = util.config self.factory = Factory(util) self.check_internet_connectivity = listeners[KEY_CHECK_INTERNET] self.go_home = listeners[KEY_HOME] self.set_modes = listeners[KEY_SET_MODES] self.linux = self.config[LINUX_PLATFORM] self.wifi_util = WiFiUtil(util) self.bluetooth_util = self.util.get_bluetooth_util() self.bounding_box = util.screen_rect layout = BorderLayout(self.bounding_box) layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, 0, 0) rows = 7 columns = 1 d = [rows, columns] MenuScreen.__init__(self, util, listeners, rows, columns, voice_assistant, d, None, page_in_title=False, show_loading=False) self.title = self.config[LABELS]["network"] self.set_title(1) center_layout = BorderLayout(self.menu_layout) center_layout.set_percent_constraints(0, 0, 47, 0) left_layout = center_layout.LEFT right_layout = center_layout.CENTER label_layout = GridLayout(left_layout) label_layout.set_pixel_constraints(rows, columns) label_layout.get_next_constraints() value_layout = GridLayout(right_layout) value_layout.set_pixel_constraints(rows, columns) value_layout.get_next_constraints() self.network_panel = Container(util, self.menu_layout, (100, 0, 0)) rect = pygame.Rect(self.menu_layout.x, self.menu_layout.y + 1, self.menu_layout.w, self.menu_layout.h - 1) bgr = Component(util, rect, 0, 0, self.menu_layout, (0, 0, 0), self.util.config[COLORS][COLOR_DARK]) bgr.name = "network.panel.bgr" self.network_panel.add_component(bgr) self.internet_label = self.add_label(label_layout, self.network_panel, 1) self.ethernet_label = self.add_label(label_layout, self.network_panel, 2) self.wifi_network_label = self.add_label(label_layout, self.network_panel, 3) self.wifi_ip_label = self.add_label(label_layout, self.network_panel, 4) self.bluetooth_label = self.add_label(label_layout, self.network_panel, 5) self.internet = self.add_value(value_layout, self.network_panel, 1) self.ethernet_ip = self.add_value(value_layout, self.network_panel, 2) self.wifi_network = self.add_value(value_layout, self.network_panel, 3) self.wifi_ip = self.add_value(value_layout, self.network_panel, 4) self.bluetooth = self.add_value(value_layout, self.network_panel, 5) self.set_menu(self.network_panel) listeners[KEY_REFRESH] = self.set_current listeners[KEY_DISCONNECT] = self.disconnect_wifi listeners[KEY_BLUETOOTH_REMOVE] = self.remove_bluetooth_devices self.navigator = NetworkNavigator(self.util, self.layout.BOTTOM, listeners) self.components.append(self.navigator) self.original_networks = None self.networks = None self.current_network = None self.current_wifi_network = None self.clicked = False
class TopicNavigator(Container): """ Collection topic navigator """ def __init__(self, util, bounding_box, listeners): """ Initializer :param util: utility object :param bounding_box: bounding box :param listeners: buttons listeners """ Container.__init__(self, util) self.factory = Factory(util) self.name = "collection.navigator" self.content = bounding_box self.content_x = bounding_box.x self.content_y = bounding_box.y self.listeners = listeners self.menu_buttons = [] self.config = util.config self.use_web = self.config[USAGE][USE_WEB] self.go_abc = listeners[KEY_ABC] self.go_keyboard = listeners[KEY_KEYBOARD_KEY] self.keyboard_callback = listeners[KEY_CALLBACK] self.bgr = util.config[COLORS][COLOR_DARK_LIGHT] self.arrow_layout = BorderLayout(bounding_box) self.arrow_layout.set_percent_constraints(0, 0, PERCENT_ARROW_WIDTH, PERCENT_ARROW_WIDTH) self.collection_topic = None self.update_observer = None self.redraw_observer = None def set_buttons(self, topic): """ Set navigator buttons depending on topic :param topic: collection topic """ if self.collection_topic == topic: return if self.menu_buttons and topic in TEXT_TOPICS and self.collection_topic in TEXT_TOPICS: self.collection_topic = topic return self.collection_topic = topic self.components = [] self.menu_buttons = [] constr = self.arrow_layout.LEFT self.left_button = self.factory.create_page_down_button(constr, "0", 40, 100) self.left_button.add_release_listener(self.listeners[GO_LEFT_PAGE]) self.add_component(self.left_button) self.menu_buttons.append(self.left_button) constr = self.arrow_layout.RIGHT self.right_button = self.factory.create_page_up_button(constr, "0", 40, 100) self.right_button.add_release_listener(self.listeners[GO_RIGHT_PAGE]) self.add_component(self.right_button) self.menu_buttons.append(self.right_button) if topic in TEXT_TOPICS: n = 7 elif topic == TYPE: n = 5 else: n = 6 layout = GridLayout(self.arrow_layout.CENTER) layout.set_pixel_constraints(1, n, 1, 0) layout.current_constraints = 0 self.add_button(KEY_HOME, KEY_HOME, layout, self.listeners[KEY_HOME]) self.add_button(KEY_BACK, KEY_BACK, layout, self.listeners[KEY_BACK]) self.add_button(COLLECTION, KEY_PARENT, layout, self.listeners[COLLECTION]) self.add_button(KEY_LIST, KEY_MENU, layout, self.listeners[KEY_LIST]) if n == 7: self.add_button(IMAGE_ABC, KEY_SETUP, layout, self.pre_abc) if n != 5: self.add_button(IMAGE_BOOK_GENRE, KEY_ROOT, layout, self.pre_keyboard) self.add_button(KEY_PLAYER, KEY_PLAY_PAUSE, layout, self.listeners[KEY_PLAYER]) if self.use_web and self.update_observer != None: self.add_observers(self.update_observer, self.redraw_observer) def add_button(self, img_name, key, layout, listener): """ Add button to the navigator :param img_name: button image name :param key: keyboard key :param layout: button layout :param listener: button listener """ c = layout.get_next_constraints() b = self.factory.create_button(img_name, key, c, listener, self.bgr, source="navigator", image_size_percent=IMAGE_SIZE_PERCENT) self.add_component(b) self.menu_buttons.append(b) def pre_abc(self, state=None): """ Set state parameters and go to ABC screen :param state: button state """ state.title = self.config[LABELS][self.collection_topic] state.visibility = False state.callback = self.keyboard_callback self.go_abc(state) def pre_keyboard(self, state=None): """ Set state parameters and go to Keyboard screen :param state: button state """ state.title = self.config[LABELS][self.collection_topic] state.visibility = False state.callback = self.keyboard_callback self.go_keyboard(state) def add_observers(self, update_observer, redraw_observer): """ Add screen observers :param update_observer: observer for updating the screen :param redraw_observer: observer to redraw the whole screen """ if self.update_observer == None: self.update_observer = update_observer self.redraw_observer = redraw_observer for b in self.menu_buttons: self.add_button_observers(b, update_observer, redraw_observer)
def __init__(self, util, listeners, voice_assistant): """ Initializer :param util: utility object :param listeners: listeners :param voice_assistant: voice assistant """ self.util = util self.config = util.config self.listeners = listeners self.factory = Factory(util) self.go_home = listeners[KEY_HOME] self.go_network = listeners[KEY_NETWORK] self.go_player = listeners[KEY_PLAYER] self.wifi_selection_listeners = [] self.bluetooth_util = self.util.get_bluetooth_util() self.bounding_box = util.screen_rect layout = BorderLayout(self.bounding_box) layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, 0, 0) d = [MENU_ROWS_BLUETOOTH, MENU_COLUMNS_BLUETOOTH] MenuScreen.__init__(self, util, listeners, MENU_ROWS_BLUETOOTH, MENU_COLUMNS_BLUETOOTH, voice_assistant, d, self.turn_page, page_in_title=False, show_loading=False) self.title = self.config[LABELS]["select.bluetooth.device"] self.set_title(1) listeners[KEY_HOME] = self.before_home listeners[KEY_NETWORK] = self.before_network listeners[KEY_PLAYER] = self.before_player listeners[KEY_REFRESH] = self.set_current self.navigator = BluetoothNavigator(self.util, self.layout.BOTTOM, listeners, PAGE_SIZE_BLUETOOTH + 1) self.add_navigator(self.navigator) self.network_button = self.navigator.get_button_by_name(KEY_NETWORK) self.left_button = self.navigator.get_button_by_name(KEY_PAGE_DOWN) self.right_button = self.navigator.get_button_by_name(KEY_PAGE_UP) m = self.factory.create_wifi_menu_button font_height = 32 font_size = int( ((self.menu_layout.h / MENU_ROWS_BLUETOOTH) / 100) * font_height) self.bluetooth_menu = MultiPageMenu(util, self.next_page, self.previous_page, self.set_title, self.reset_title, self.go_to_page, m, MENU_ROWS_BLUETOOTH, MENU_COLUMNS_BLUETOOTH, None, (0, 0, 0), self.menu_layout, align=ALIGN_LEFT, font_size=font_size) self.set_menu(self.bluetooth_menu) self.devices = None self.networks = None self.sort_direction = False self.current_device = None
def __init__(self, util, name, bgr, slider_color, img_knob, img_knob_on, key_incr, key_decr, key_knob, bb): """ Initializer :param util: utility object :param name: slider name :param bgr: slider background color :param slider_color: slider center line color :param img_knob: knob image :param img_knob_on: knob image in on state :param key_incr: keyboard key associated with slider increment action :param key_decr: keyboard key associated with slider decrement action :param key_knob: keyboard key associated with single click on knob :param bb: slider bounding box """ Container.__init__(self, util, background=bgr, bounding_box=bb) self.util = util self.config = util.config self.lock = RLock() self.CURRENT_TIME_LAYER = 3 self.TOTAL_TIME_LAYER = 2 # don't increase the following number too much as it affects UV Meter screen-saver performance self.LOOP_CYCLES_PER_SECOND = 5 self.CYCLE_TIME = 1 / self.LOOP_CYCLES_PER_SECOND self.active = True comp = Component(self.util, bb) comp.name = name + "bgr" comp.bgr = bgr self.add_component(comp) layout = BorderLayout(bb) layout.set_percent_constraints(0.0, 0.0, 20.0, 20.0) self.current_time_name = name + "current" self.total_time_name = name + "total" self.current_time_layout = layout.LEFT self.total_time_layout = layout.RIGHT self.slider = Slider(util, name + "slider", bgr, slider_color, img_knob, img_knob_on, None, key_incr, key_decr, key_knob, layout.CENTER, False) self.slider.add_slide_listener(self.slider_action_handler) self.total_track_time = 0 self.seek_time = 0 self.add_component(self.slider) c = Component(self.util, None) # init total time layer self.add_component(c) c = Component(self.util, None) # init current time layer self.add_component(c) self.seek_listeners = [] self.start_timer_listeners = [] self.stop_timer_listeners = [] self.update_seek_listeners = True self.use_web = self.config[USAGE][USE_WEB] self.stop_timer() thread = Thread(target=self.start_loop) thread.start()
def __init__(self, util): """ Initializer :param util: utility object """ self.util = util self.config = util.config self.config_class = util.config_class factory = Factory(util) self.name = "about.screen" self.color_web_bgr = self.config[COLORS][COLOR_WEB_BGR] color_logo = self.config[COLORS][COLOR_CONTRAST] color_status = self.config[COLORS][COLOR_LOGO] opacity = self.config[BACKGROUND][MENU_BGR_OPACITY] c = (self.color_web_bgr[0], self.color_web_bgr[1], self.color_web_bgr[2], opacity) cont = util.screen_rect img_filename = None bg = self.util.get_background("about", c) self.bgr_type = bg[0] if bg[2]: self.bgr = bg[1] cont = bg[2] else: self.bgr = (bg[1][0], bg[1][1], bg[1][2]) img_filename = bg[3] self.bgr_key = bg[5] Container.__init__(self, util, bounding_box=util.screen_rect, background=self.color_web_bgr, content=cont, image_filename=img_filename) self.start_listeners = [] self.installed_release = self.config[RELEASE] self.installed_edition = self.installed_release[EDITION_NAME] self.installed_year = self.installed_release[RELEASE_YEAR] self.installed_month = self.installed_release[RELEASE_MONTH] self.installed_day = self.installed_release[RELEASE_DAY] layout = BorderLayout(self.bounding_box) layout.set_percent_constraints(0, PERCENT_FOOTER_HEIGHT, 0, 0) font_size = int((layout.BOTTOM.h * PERCENT_FOOTER_FONT)/100.0) button = factory.create_image_button("peppy", bounding_box=layout.CENTER, bgr=bg[1], image_size_percent=68, selected=False) if bg[2]: button.parent_screen = bg[2] else: button.parent_screen = layout.CENTER x = layout.CENTER.w/2 - button.components[1].content.get_size()[0]/2 y = layout.CENTER.h/2 - button.components[1].content.get_size()[1]/2 button.components[1].content_x = x button.components[1].content_y = y self.add_component(button) bottom_bgr = pygame.Rect(layout.BOTTOM.x, layout.BOTTOM.y - 1, layout.BOTTOM.w, layout.BOTTOM.h + 2) comp = Component(self.util, bottom_bgr, bb=bottom_bgr, bgr=bg[1]) comp.name = "txt.bgr" self.add_component(comp) text_layout = pygame.Rect(layout.BOTTOM.x, layout.BOTTOM.y - int((self.bounding_box.h * 5) / 100), layout.BOTTOM.w, layout.BOTTOM.h) if self.util.connected_to_internet and self.config[USAGE][USE_CHECK_FOR_UPDATES]: bottom_layout = GridLayout(text_layout) bottom_layout.set_pixel_constraints(2, 1) line_top = bottom_layout.get_next_constraints() line_bottom = bottom_layout.get_next_constraints() else: line_top = text_layout transparent = (0, 0, 0, 0) self.release = factory.create_output_text("installed", line_top, transparent, color_logo, font_size, full_width=True, valign=V_ALIGN_BOTTOM) self.release.parent_screen = bg[2] if bg[2]: self.release.parent_screen = bg[2] else: self.release.parent_screen = line_top self.add_component(self.release) if self.util.connected_to_internet and self.config[USAGE][USE_CHECK_FOR_UPDATES]: self.status = factory.create_output_text("status", line_bottom, transparent, color_status, font_size, full_width=True, valign=V_ALIGN_TOP) self.status.parent_screen = bg[2] self.add_component(self.status) self.new_release = self.get_new_release()
def __init__(self, util, bounding_box, name, items, arrow_items=None): """ Initializer :param util: utility object :param bounding_box: bounding box :param name: navigator name :param items: dictionary with button details :param arrow_items: dictionary with arrow buttons details """ Container.__init__(self, util) self.factory = Factory(util) self.name = name self.content = bounding_box self.content_x = bounding_box.x self.content_y = bounding_box.y self.buttons = [] if arrow_items: arrow_layout = BorderLayout(bounding_box) left_arrow = arrow_items[0] listeners = left_arrow[LISTENER] arrow_layout.set_percent_constraints(0, 0, PERCENT_ARROW_WIDTH, PERCENT_ARROW_WIDTH) constr = arrow_layout.LEFT button = self.factory.create_page_down_button(constr, "0", 40, 100) button.add_release_listener(listeners[0]) self.add_component(button) self.buttons.append(button) layout = GridLayout(arrow_layout.CENTER) else: layout = GridLayout(bounding_box) layout.set_pixel_constraints(1, len(items), 1, 0) layout.current_constraints = 0 image_size = 64 b = util.config[BACKGROUND][FOOTER_BGR_COLOR] for item in items: constr = layout.get_next_constraints() image_name = item[IMAGE_NAME] listeners = item[LISTENER] keybiard_key = item[KEYBOARD_KEY] source = item[SOURCE] if len(listeners) == 1: button = self.factory.create_button( image_name, keybiard_key, constr, listeners[0], b, image_size_percent=image_size, source=source) else: button = self.factory.create_button( image_name, keybiard_key, constr, None, b, image_size_percent=image_size, source=source) for listener in listeners: button.add_release_listener(listener) self.add_component(button) self.buttons.append(button) if arrow_items: right_arrow = arrow_items[1] listeners = right_arrow[LISTENER] constr = arrow_layout.RIGHT button = self.factory.create_page_up_button(constr, "0", 40, 100) button.add_release_listener(listeners[0]) self.add_component(button) self.buttons.append(button)