class MenuShow(Show): def __init__(self, show_id, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile, command_callback): """ show_id - index of the top level show caling this (for debug only) show_params - dictionary section for the menu canvas - the canvas that the menu is to be written on showlist - the showlist pp_dir - Pi Presents directory pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory """ # init the common bits Show.base__init__( self, show_id, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile, command_callback ) # instatiatate the screen driver - used only to access enable and hide click areas self.sr = ScreenDriver() self.controlsmanager = ControlsManager() # init variables self.show_timeout_timer = None self.track_timeout_timer = None self.next_track_signal = False self.next_track = None self.menu_index = 0 self.menu_showing = True self.req_next = "" def play(self, end_callback, show_ready_callback, parent_kickback_signal, level, controls_list): """ displays the menu end_callback - function to be called when the menu exits show_ready_callback - callback when menu is ready to display (not used) level is 0 when the show is top level (run from [start] or from show control) parent_kickback_signal - not used other than it being passed to a show """ # need to instantiate the medialist here as not using gapshow self.medialist = MediaList("ordered") Show.base_play(self, end_callback, show_ready_callback, parent_kickback_signal, level, controls_list) self.mon.trace(self, self.show_params["show-ref"]) # parse the show and track timeouts reason, message, self.show_timeout = Show.calculate_duration(self, self.show_params["show-timeout"]) if reason == "error": self.mon.err(self, "Show Timeout has bad time: " + self.show_params["show-timeout"]) self.end("error", "show timeout, bad time") reason, message, self.track_timeout = Show.calculate_duration(self, self.show_params["track-timeout"]) if reason == "error": self.mon.err(self, "Track Timeout has bad time: " + self.show_params["track-timeout"]) self.end("error", "track timeout, bad time") # and delete eggtimer if self.previous_shower is not None: self.previous_shower.delete_eggtimer() # and display the menu self.do_menu_track() # ******************** # respond to inputs. # ******************** # exit received from another concurrent show def exit(self): self.stop_timers() Show.base_exit(self) # show timeout happened def show_timeout_stop(self): self.stop_timers() Show.base_show_timeout_stop(self) # terminate Pi Presents def terminate(self): self.stop_timers() Show.base_terminate(self) def handle_input_event(self, symbol): Show.base_handle_input_event(self, symbol) def handle_input_event_this_show(self, symbol): # menushow has only internal operation operation = self.base_lookup_control(symbol, self.controls_list) self.do_operation(operation) def do_operation(self, operation): # service the standard inputs for this show self.mon.trace(self, operation) if operation == "exit": self.exit() elif operation == "stop": self.stop_timers() if self.current_player is not None: if self.menu_showing is True and self.level != 0: # if quiescent then set signal to stop the show when track has stopped self.user_stop_signal = True self.current_player.input_pressed("stop") elif operation in ("up", "down"): # stop show timeout if self.show_timeout_timer is not None: self.canvas.after_cancel(self.show_timeout_timer) # and start it again if self.show_timeout != 0: self.show_timeout_timer = self.canvas.after(self.show_timeout * 1000, self.show_timeout_stop) if operation == "up": self.previous() else: self.next() elif operation == "play": self.next_track_signal = True self.next_track = self.medialist.selected_track() self.current_player.stop() # cancel show timeout if self.show_timeout_timer is not None: self.canvas.after_cancel(self.show_timeout_timer) self.show_timeout_timer = None # stop current track (the menuplayer) if running or just start the next track if self.current_player is not None: self.current_player.input_pressed("stop") else: self.what_next_after_showing() elif operation in ("no-command", "null"): return elif operation == "pause": if self.current_player is not None: self.current_player.input_pressed(operation) elif operation[0:4] == "omx-" or operation[0:6] == "mplay-" or operation[0:5] == "uzbl-": if self.current_player is not None: self.current_player.input_pressed(operation) def next(self): # remove highlight if self.current_player.__class__.__name__ == "MenuPlayer": self.current_player.highlight_menu_entry(self.menu_index, False) self.medialist.next("ordered") if self.menu_index == self.menu_length - 1: self.menu_index = 0 else: self.menu_index += 1 # and highlight the new entry self.current_player.highlight_menu_entry(self.menu_index, True) def previous(self): # remove highlight if self.current_player.__class__.__name__ == "MenuPlayer": self.current_player.highlight_menu_entry(self.menu_index, False) if self.menu_index == 0: self.menu_index = self.menu_length - 1 else: self.menu_index -= 1 self.medialist.previous("ordered") # and highlight the new entry self.current_player.highlight_menu_entry(self.menu_index, True) # ********************* # Sequencing # ********************* def track_timeout_callback(self): self.do_operation("stop") def do_menu_track(self): self.menu_showing = True self.mon.trace(self, "") # start show timeout alarm if required if self.show_timeout != 0: self.show_timeout_timer = self.canvas.after(self.show_timeout * 1000, self.show_timeout_stop) # init the index used to hiighlight the selected menu entry by menuplayer self.menu_index = 0 index = self.medialist.index_of_track(self.show_params["menu-track-ref"]) if index == -1: self.mon.err(self, "'menu-track' not in medialist: " + self.show_params["menu-track-ref"]) self.end("error", "menu-track not in medialist: ") return # make the medialist available to the menuplayer for cursor scrolling self.show_params["medialist_obj"] = self.medialist # load the menu track self.start_load_show_loop(self.medialist.track(index)) # ********************* # Playing show or track loop # ********************* def start_load_show_loop(self, selected_track): # shuffle players Show.base_shuffle(self) self.mon.trace(self, "") self.display_eggtimer() # get control bindings for this show # needs to be done for each track as track can override the show controls if self.show_params["disable-controls"] == "yes": self.controls_list = [] else: reason, message, self.controls_list = self.controlsmanager.get_controls(self.show_params["controls"]) if reason == "error": self.mon.err(self, message) self.end("error", "error in controls") return # load the track or show Show.base_load_track_or_show(self, selected_track, self.what_next_after_load, self.end_shower, False) # track has loaded (menu or otherwise) so show it. def what_next_after_load(self, status, message): # get the calculated length of the menu for the loaded menu track if self.current_player.__class__.__name__ == "MenuPlayer": if self.medialist.display_length() == 0: self.req_next = "error" self.what_next_after_showing() return self.medialist.start() self.menu_index = 0 self.menu_length = self.current_player.menu_length self.current_player.highlight_menu_entry(self.menu_index, True) self.mon.trace(self, " - load complete with status: " + status + " message: " + message) if self.current_player.play_state == "load-failed": self.req_next = "error" self.what_next_after_showing() else: if ( self.show_timeout_signal is True or self.terminate_signal is True or self.exit_signal is True or self.user_stop_signal is True ): self.what_next_after_showing() else: self.mon.trace(self, "") self.current_player.show(self.track_ready_callback, self.finished_showing, self.closed_after_showing) def finished_showing(self, reason, message): # showing has finished with 'pause at end', showing the next track will close it after next has started showing self.mon.trace(self, "") self.mon.log(self, "pause at end of showing track with reason: " + reason + " and message: " + message) self.sr.hide_click_areas(self.controls_list) if self.current_player.play_state == "show-failed": self.req_next = "error" else: self.req_next = "finished-player" self.what_next_after_showing() def closed_after_showing(self, reason, message): # showing has finished with closing of player but track instance is alive for hiding the x_content self.mon.trace(self, "") self.mon.log(self, "Closed after showing track with reason: " + reason + " and message: " + message) self.sr.hide_click_areas(self.controls_list) if self.current_player.play_state == "show-failed": self.req_next = "error" else: self.req_next = "closed-player" self.what_next_after_showing() # subshow or child show has ended def end_shower(self, show_id, reason, message): self.mon.log( self, self.show_params["show-ref"] + " " + str(self.show_id) + ": Returned from shower with " + reason + " " + message, ) self.sr.hide_click_areas(self.controls_list) self.req_next = reason Show.base_end_shower(self) self.what_next_after_showing() # at the end of a track check for terminations else re-display the menu def what_next_after_showing(self): self.mon.trace(self, "") # cancel track timeout timer if self.track_timeout_timer is not None: self.canvas.after_cancel(self.track_timeout_timer) self.track_timeout_timer = None # need to terminate? if self.terminate_signal is True: self.terminate_signal = False # set what to do when closed or unloaded self.ending_reason = "killed" Show.base_close_or_unload(self) elif self.req_next == "error": self.req_next = "" # set what to do after closed or unloaded self.ending_reason = "error" Show.base_close_or_unload(self) # show timeout elif self.show_timeout_signal is True: self.show_timeout_signal = False # set what to do when closed or unloaded self.ending_reason = "show-timeout" Show.base_close_or_unload(self) # used by exit for stopping show from other shows. elif self.exit_signal is True: self.exit_signal = False self.ending_reason = "exit" Show.base_close_or_unload(self) # user wants to stop elif self.user_stop_signal is True: self.user_stop_signal = False self.ending_reason = "user-stop" Show.base_close_or_unload(self) elif self.next_track_signal is True: self.next_track_signal = False self.menu_showing = False # start timeout for the track if required if self.track_timeout != 0: self.track_timeout_timer = self.canvas.after(self.track_timeout * 1000, self.track_timeout_callback) self.start_load_show_loop(self.next_track) else: # no stopping the show required so re-display the menu self.do_menu_track() # ********************* # Interface with other shows/players to reduce black gaps # ********************* # called just before a track is shown to remove the previous track from the screen # and if necessary close it def track_ready_callback(self, enable_show_background): self.delete_eggtimer() if self.show_params["disable-controls"] != "yes": # merge controls from the track controls_text = self.current_player.get_links() reason, message, track_controls = self.controlsmanager.parse_controls(controls_text) if reason == "error": self.mon.err(self, message + " in track: " + self.current_player.track_params["track-ref"]) self.req_next = "error" self.what_next_after_showing() self.controlsmanager.merge_controls(self.controls_list, track_controls) self.sr.enable_click_areas(self.controls_list) Show.base_track_ready_callback(self, enable_show_background) # callback from begining of a subshow, provide previous shower player to called show def subshow_ready_callback(self): return Show.base_subshow_ready_callback(self) # ********************* # Ending the show # ********************* def end(self, reason, message): self.stop_timers() Show.base_end(self, reason, message) def stop_timers(self): if self.track_timeout_timer is not None: self.canvas.after_cancel(self.track_timeout_timer) self.track_timeout_timer = None if self.show_timeout_timer is not None: self.canvas.after_cancel(self.show_timeout_timer) self.show_timeout_timer = None
class MediaShow: # ******************* # External interface # ******************** def __init__(self, show, canvas, showlist, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the dictionary fo the show to be played pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory """ self.mon=Monitor() self.mon.on() #instantiate arguments self.show =show self.showlist=showlist self.canvas=canvas self.pp_home=pp_home self.pp_profile=pp_profile # open resources self.rr=ResourceReader() # Init variables self.player=None self.shower=None self._poll_for_interval_timer=None self._poll_for_continue_timer=None self._waiting_for_interval=False self._interval_timer=None self.error=False self._interval_timer_signal=False self._end_mediashow_signal=False self._next_track_signal=False self._previous_track_signal=False self._play_child_signal = False self._req_next='nil' self._state='closed' def play(self,end_callback,ready_callback=None, top=False,command='nil'): """ displays the mediashow end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ #instantiate the arguments self._end_callback=end_callback self._ready_callback=ready_callback self.top=top self.command=command self.mon.log(self,"Starting show: " + self.show['show-ref']) # check data files are available. self.media_file = self.pp_profile + "/" + self.show['medialist'] if not os.path.exists(self.media_file): self.mon.err(self,"Medialist file not found: "+ self.media_file) self._end('error',"Medialist file not found") #create a medialist for the mediashow and read it. self.medialist=MediaList() if self.medialist.open_list(self.media_file,self.showlist.sissue())==False: self.mon.err(self,"Version of medialist different to Pi Presents") self._end('error',"Version of medialist different to Pi Presents") self._wait_for_trigger() # respond to key presses. def key_pressed(self,key_name): self.mon.log(self,"received key: " + key_name) if key_name=='': pass elif key_name=='escape': # if next lower show is running pass down to stop the show and lower level if self.shower<>None: self.shower.key_pressed(key_name) # if not at top stop the show else: if self.top == False: self._end_mediashow_signal=True # and if a track is running stop that first if self.player<>None: self.player.key_pressed(key_name) else: # at top level in a manual presentation stop the track if self.show['progress']=='manual': if self.player<>None: self.player.key_pressed(key_name) elif key_name in ('up','down'): # if child or sub-show is running and is a show pass to show, track does not use up/down # otherwise use keys for next or previous if self.shower<>None: self.shower.key_pressed(key_name) else: if key_name=='up': self._previous() else: self._next() elif key_name=='return': # if child show or sub-show is running and is show - pass down- player does not use return # ELSE use Return to start child or to start the show if waiting if self.shower<>None: self.shower.key_pressed(key_name) else: if self._state=='playing': if self.show['has-child']=='yes': self._play_child_signal=True # and stop the current track if its running if self.player<>None: self.player.key_pressed("escape") else: self._start_show() elif key_name=='pir': self._start_show() elif key_name in ('p',' '): # pass down if show or track running. if self.shower<>None: self.shower.key_pressed(key_name) elif self.player<>None: self.player.key_pressed(key_name) def button_pressed(self,button,edge): if button=='play': self.key_pressed("return") elif button =='up': self.key_pressed("up") elif button=='down': self.key_pressed("down") elif button=='stop': self.key_pressed("escape") elif button=='pause': self.key_pressed('p') elif button=='PIR': self.key_pressed('pir') # kill or error def terminate(self,reason): if self.shower<>None: self.mon.log(self,"sent terminate to shower") self.shower.terminate(reason) elif self.player<>None: self.mon.log(self,"sent terminate to player") self.player.terminate(reason) else: self._end(reason,'terminated without terminating shower or player') def _tidy_up(self): if self._poll_for_continue_timer<>None: self.canvas.after_cancel(self._poll_for_continue_timer) self._poll_for_continue_timer=None if self._poll_for_interval_timer<>None: self.canvas.after_cancel(self._poll_for_interval_timer) self._poll_for_interval_timer=None if self._interval_timer<>None: self.canvas.after_cancel(self._interval_timer) self._interval_timer=None def resource(self,section,item): value=self.rr.get(section,item) if value==False: self.mon.err(self, "resource: "+section +': '+ item + " not found" ) self.terminate("error") else: return value # *************************** # Respond to key/button presses # *************************** def _stop(self,message): self._end_mediashow_signal=True if self._interval_timer<>None: self.canvas.after_cancel(self._interval_timer) def _next(self): # stop track if running and set signal self._next_track_signal=True if self.shower<>None: self.shower.key_pressed("escape") else: if self.player<>None: self.player.key_pressed("escape") def _previous(self): self._previous_track_signal=True if self.shower<>None: self.shower.key_pressed("escape") else: if self.player<>None: self.player.key_pressed("escape") # *************************** # end of show functions # *************************** def _end(self,reason,message): self._end_mediashow_signal=False self.mon.log(self,"Ending Mediashow: "+ self.show['show-ref']) self._tidy_up() self._end_callback(reason,message) self=None return # *************************** # Show sequencer # *************************** def _wait_for_trigger(self): self._state='waiting' if self.ready_callback<>None: self.ready_callback() self.mon.log(self,"Waiting for trigger: "+ self.show['trigger']) if self.show['trigger']=="button": # blank screen waiting for trigger if auto, otherwise display something if self.show['progress']=="manual": text= self.resource('mediashow','m01') else: text="" self.display_message(self.canvas,'text',text,0,self._start_show) elif self.show['trigger']=="PIR": # blank screen waiting for trigger text = self.resource('mediashow','m02') self.display_message(self.canvas,'text',text,0,self._start_show) elif self.show['trigger']=="start": self._start_show() else: self.mon.err(self,"Unknown trigger: "+ self.show['trigger']) self._end('error',"Unknown trigger type") def _start_show(self): self._state='playing' self._direction='forward' # start interval timer if self.show['repeat']=="interval" and self.show['repeat-interval']<>0: self._interval_timer_signal=False self._interval_timer=self.canvas.after(int(self.show['repeat-interval'])*1000,self._end_interval_timer) # and play the first track unless commanded otherwise if self.command=='backward': self.medialist.finish() else: self.medialist.start() self._play_selected_track(self.medialist.selected_track()) def _what_next(self): self._direction='forward' # user wants to end, wait for any shows or tracks to have ended then end show if self._end_mediashow_signal==True: if self.player==None and self.shower==None: self._end_mediashow_signal=False self._end('normal',"show ended by user") else: pass #returning from a subshow needing to move onward elif self._req_next=='do-next': self._req_next='nil' self.medialist.next() self._play_selected_track(self.medialist.selected_track()) #returning from a subshow needing to move backward elif self._req_next=='do-previous': self._req_next='nil' self._direction='backward' self.medialist.previous() self._play_selected_track(self.medialist.selected_track()) # user wants to play child elif self._play_child_signal == True: self._play_child_signal=False index = self.medialist.index_of_track('pp-child-show') if index >=0: #don't select the track as need to preserve mediashow sequence. child_track=self.medialist.track(index) self._display_eggtimer(self.resource('mediashow','m07')) self._play_selected_track(child_track) else: self.mon.err(self,"Child show not found in medialist: "+ self.show['pp-child-show']) self._end('error',"child show not found in medialist") # skip to next track on user input elif self._next_track_signal==True: self._next_track_signal=False if self.medialist.at_end()==True: if self.show['sequence']=="ordered" and self.show['repeat']=='oneshot' and self.top==False: self._end('do-next',"Return from Sub Show") else: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) else: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) # skip to previous track on user input elif self._previous_track_signal==True: self._previous_track_signal=False self._direction='backward' if self.medialist.at_start()==True: if self.show['sequence']=="ordered" and self.show['repeat']=='oneshot' and self.top==False: self._end('do-previous',"Return from Sub Show") else: self.medialist.previous() self._play_selected_track(self.medialist.selected_track()) else: self.medialist.previous() self._play_selected_track(self.medialist.selected_track()) # track is finished and we are on auto elif self.show['progress']=="auto": if self.medialist.at_end()==True: if self.show['sequence']=="ordered" and self.show['repeat']=='oneshot' and self.top==False: self._end('do-next',"Return from Sub Show") #### elif elif self.show['sequence']=="ordered" and self.show['repeat']=='oneshot' and self.top==True: self._wait_for_trigger() elif self._waiting_for_interval==True: if self._interval_timer_signal==True: self._interval_timer_signal=False self._waiting_for_interval=False self._start_show() else: self._poll_for_interval_timer=self.canvas.after(1000,self._what_next) elif self.show['sequence']=="ordered" and self.show['repeat']=='interval' and int(self.show['repeat-interval'])>0: self._waiting_for_interval=True self._poll_for_interval_timer=self.canvas.after(1000,self._what_next) elif self.show['sequence']=="ordered" and self.show['repeat']=='interval' and int(self.show['repeat-interval'])==0: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) else: self.mon.err(self,"Unhandled playing event: ") self._end('error',"Unhandled playing event") else: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) # track has finished and we are on manual progress elif self.show['progress']=="manual": self._delete_eggtimer() self._display_eggtimer(self.resource('mediashow','m03')) self._poll_for_continue_timer=self.canvas.after(500,self._what_next) else: #unhandled state self.mon.err(self,"Unhandled playing event: ") self._end('error',"Unhandled playing event") def _end_interval_timer(self): self._interval_timer_signal=True # *************************** # Dispatching to Players/Shows # *************************** # used to display internal messages in situations where a medialist entry could be used. def display_message(self,canvas,source,content,duration,_display_message_callback): self._display_message_callback=_display_message_callback tp={'duration':duration,'message-colour':'white','message-font':'Helvetica 20 bold'} self.player=MessagePlayer(canvas,tp,tp) self.player.play(content,self._display_message_end,None) def _display_message_end(self,reason,message): self.player=None if reason in ('error','killed'): self._end(reason,message) else: self._display_message_callback() def complete_path(self,selected_track): # complete path of the filename of the selected entry track_file = selected_track['location'] if track_file[0]=="+": track_file=self.pp_home+track_file[1:] self.mon.log(self,"Track to play is: "+ track_file) return track_file def _play_selected_track(self,selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ self.canvas.delete(ALL) if self.show['progress']=="manual": self._display_eggtimer(self.resource('mediashow','m04')) # is menu required if self.show['has-child']=="yes": enable_child=True else: enable_child=False #dispatch track by type self.player=None self.shower=None track_type = selected_track['type'] self.mon.log(self,"Track type is: "+ track_type) if track_type=="video": # create a videoplayer track_file=self.complete_path(selected_track) self.player=VideoPlayer(self.canvas,self.show,selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type=="image": track_file=self.complete_path(selected_track) # images played from menus don't have children self.player=ImagePlayer(self.canvas,self.show,selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type=="message": # bit odd because MessagePlayer is used internally to display text. text=selected_track['text'] self.player=MessagePlayer(self.canvas,self.show,selected_track) self.player.play(text, self.end_player, self.ready_callback, enable_menu=enable_child ) elif track_type=="show": # get the show from the showlist index = self.showlist.index_of_show(selected_track['sub-show']) if index >=0: self.showlist.select(index) selected_show=self.showlist.selected_show() else: self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show']) self._end('error',"Unknown show") if selected_show['type']=="mediashow": self.shower= MediaShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower,top=False,command=self._direction) elif selected_show['type']=="liveshow": self.shower= LiveShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower,top=False,command='nil') elif selected_show['type']=="menu": self.shower= MenuShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower,top=False,command='nil') else: self.mon.err(self,"Unknown Show Type: "+ selected_show['type']) self._end('error'"Unknown show type") else: self.mon.err(self,"Unknown Track Type: "+ track_type) self._end('error',"Unknown track type") def ready_callback(self): self._delete_eggtimer() def end_player(self,reason,message): self._req_next='nil' self.mon.log(self,"Returned from player with message: "+ message) self.player=None if reason in("killed","error"): self._end(reason,message) elif self.show['progress']=="manual": self._display_eggtimer(self.resource('mediashow','m05')) self._req_next=reason self._what_next() else: self._req_next=reason self._what_next() def end_shower(self,reason,message): self._req_next='nil' self.mon.log(self,"Returned from shower with message: "+ message) self.shower=None if reason in("killed","error"): self._end(reason,message) elif self.show['progress']=="manual": self._display_eggtimer(self.resource('mediashow','m06')) self._req_next=reason self._what_next() else: self._req_next=reason self._what_next() def _display_eggtimer(self,text): self.canvas.create_text(int(self.canvas['width'])/2, int(self.canvas['height'])/2, text= text, fill='white', font="Helvetica 20 bold") self.canvas.update_idletasks( ) def _delete_eggtimer(self): self.canvas.delete(ALL)
class MediaShow: # ******************* # External interface # ******************** def __init__(self, show, canvas, showlist, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the dictionary fo the show to be played pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory """ self.mon = Monitor() self.mon.on() #instantiate arguments self.show = show self.showlist = showlist self.canvas = canvas self.pp_home = pp_home self.pp_profile = pp_profile # open resources self.rr = ResourceReader() # Init variables self.player = None self.shower = None self._poll_for_interval_timer = None self._poll_for_continue_timer = None self._waiting_for_interval = False self._interval_timer = None self.error = False self._interval_timer_signal = False self._end_mediashow_signal = False self._next_track_signal = False self._previous_track_signal = False self._play_child_signal = False self._req_next = 'nil' self._state = 'closed' def play(self, end_callback, ready_callback=None, top=False, command='nil'): """ displays the mediashow end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ #instantiate the arguments self._end_callback = end_callback self._ready_callback = ready_callback self.top = top self.command = command self.mon.log(self, "Starting show: " + self.show['show-ref']) # check data files are available. self.media_file = self.pp_profile + "/" + self.show['medialist'] if not os.path.exists(self.media_file): self.mon.err(self, "Medialist file not found: " + self.media_file) self._end('error', "Medialist file not found") #create a medialist for the mediashow and read it. self.medialist = MediaList() if self.medialist.open_list(self.media_file, self.showlist.sissue()) == False: self.mon.err(self, "Version of medialist different to Pi Presents") self._end('error', "Version of medialist different to Pi Presents") self._wait_for_trigger() # respond to key presses. def key_pressed(self, key_name): self.mon.log(self, "received key: " + key_name) if key_name == '': pass elif key_name == 'escape': # if next lower show is running pass down to stop the show and lower level if self.shower <> None: self.shower.key_pressed(key_name) # if not at top stop the show else: if self.top == False: self._end_mediashow_signal = True # and if a track is running stop that first if self.player <> None: self.player.key_pressed(key_name) else: # at top level in a manual presentation stop the track if self.show['progress'] == 'manual': if self.player <> None: self.player.key_pressed(key_name) elif key_name in ('up', 'down'): # if child or sub-show is running and is a show pass to show, track does not use up/down # otherwise use keys for next or previous if self.shower <> None: self.shower.key_pressed(key_name) else: if key_name == 'up': self._previous() else: self._next() elif key_name == 'return': # if child show or sub-show is running and is show - pass down- player does not use return # ELSE use Return to start child or to start the show if waiting if self.shower <> None: self.shower.key_pressed(key_name) else: if self._state == 'playing': if self.show['has-child'] == 'yes': self._play_child_signal = True # and stop the current track if its running if self.player <> None: self.player.key_pressed("escape") else: self._start_show() elif key_name == 'pir': self._start_show() elif key_name in ('p', ' '): # pass down if show or track running. if self.shower <> None: self.shower.key_pressed(key_name) elif self.player <> None: self.player.key_pressed(key_name) def button_pressed(self, button, edge): if button == 'play': self.key_pressed("return") elif button == 'up': self.key_pressed("up") elif button == 'down': self.key_pressed("down") elif button == 'stop': self.key_pressed("escape") elif button == 'pause': self.key_pressed('p') elif button == 'PIR': self.key_pressed('pir') # kill or error def terminate(self, reason): if self.shower <> None: self.mon.log(self, "sent terminate to shower") self.shower.terminate(reason) elif self.player <> None: self.mon.log(self, "sent terminate to player") self.player.terminate(reason) else: self._end(reason, 'terminated without terminating shower or player') def _tidy_up(self): if self._poll_for_continue_timer <> None: self.canvas.after_cancel(self._poll_for_continue_timer) self._poll_for_continue_timer = None if self._poll_for_interval_timer <> None: self.canvas.after_cancel(self._poll_for_interval_timer) self._poll_for_interval_timer = None if self._interval_timer <> None: self.canvas.after_cancel(self._interval_timer) self._interval_timer = None def resource(self, section, item): value = self.rr.get(section, item) if value == False: self.mon.err(self, "resource: " + section + ': ' + item + " not found") self.terminate("error") else: return value # *************************** # Respond to key/button presses # *************************** def _stop(self, message): self._end_mediashow_signal = True if self._interval_timer <> None: self.canvas.after_cancel(self._interval_timer) def _next(self): # stop track if running and set signal self._next_track_signal = True if self.shower <> None: self.shower.key_pressed("escape") else: if self.player <> None: self.player.key_pressed("escape") def _previous(self): self._previous_track_signal = True if self.shower <> None: self.shower.key_pressed("escape") else: if self.player <> None: self.player.key_pressed("escape") # *************************** # end of show functions # *************************** def _end(self, reason, message): self._end_mediashow_signal = False self.mon.log(self, "Ending Mediashow: " + self.show['show-ref']) self._tidy_up() self._end_callback(reason, message) self = None return # *************************** # Show sequencer # *************************** def _wait_for_trigger(self): self._state = 'waiting' if self.ready_callback <> None: self.ready_callback() self.mon.log(self, "Waiting for trigger: " + self.show['trigger']) if self.show['trigger'] == "button": # blank screen waiting for trigger if auto, otherwise display something if self.show['progress'] == "manual": text = self.resource('mediashow', 'm01') else: text = "" self.display_message(self.canvas, 'text', text, 0, self._start_show) elif self.show['trigger'] == "PIR": # blank screen waiting for trigger text = self.resource('mediashow', 'm02') self.display_message(self.canvas, 'text', text, 0, self._start_show) elif self.show['trigger'] == "start": self._start_show() else: self.mon.err(self, "Unknown trigger: " + self.show['trigger']) self._end('error', "Unknown trigger type") def _start_show(self): self._state = 'playing' self._direction = 'forward' # start interval timer if self.show[ 'repeat'] == "interval" and self.show['repeat-interval'] <> 0: self._interval_timer_signal = False self._interval_timer = self.canvas.after( int(self.show['repeat-interval']) * 1000, self._end_interval_timer) # and play the first track unless commanded otherwise if self.command == 'backward': self.medialist.finish() else: self.medialist.start() self._play_selected_track(self.medialist.selected_track()) def _what_next(self): self._direction = 'forward' # user wants to end, wait for any shows or tracks to have ended then end show if self._end_mediashow_signal == True: if self.player == None and self.shower == None: self._end_mediashow_signal = False self._end('normal', "show ended by user") else: pass #returning from a subshow needing to move onward elif self._req_next == 'do-next': self._req_next = 'nil' self.medialist.next() self._play_selected_track(self.medialist.selected_track()) #returning from a subshow needing to move backward elif self._req_next == 'do-previous': self._req_next = 'nil' self._direction = 'backward' self.medialist.previous() self._play_selected_track(self.medialist.selected_track()) # user wants to play child elif self._play_child_signal == True: self._play_child_signal = False index = self.medialist.index_of_track('pp-child-show') if index >= 0: #don't select the track as need to preserve mediashow sequence. child_track = self.medialist.track(index) self._display_eggtimer(self.resource('mediashow', 'm07')) self._play_selected_track(child_track) else: self.mon.err( self, "Child show not found in medialist: " + self.show['pp-child-show']) self._end('error', "child show not found in medialist") # skip to next track on user input elif self._next_track_signal == True: self._next_track_signal = False if self.medialist.at_end() == True: if self.show['sequence'] == "ordered" and self.show[ 'repeat'] == 'oneshot' and self.top == False: self._end('do-next', "Return from Sub Show") else: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) else: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) # skip to previous track on user input elif self._previous_track_signal == True: self._previous_track_signal = False self._direction = 'backward' if self.medialist.at_start() == True: if self.show['sequence'] == "ordered" and self.show[ 'repeat'] == 'oneshot' and self.top == False: self._end('do-previous', "Return from Sub Show") else: self.medialist.previous() self._play_selected_track(self.medialist.selected_track()) else: self.medialist.previous() self._play_selected_track(self.medialist.selected_track()) # track is finished and we are on auto elif self.show['progress'] == "auto": if self.medialist.at_end() == True: if self.show['sequence'] == "ordered" and self.show[ 'repeat'] == 'oneshot' and self.top == False: self._end('do-next', "Return from Sub Show") #### elif elif self.show['sequence'] == "ordered" and self.show[ 'repeat'] == 'oneshot' and self.top == True: self._wait_for_trigger() elif self._waiting_for_interval == True: if self._interval_timer_signal == True: self._interval_timer_signal = False self._waiting_for_interval = False self._start_show() else: self._poll_for_interval_timer = self.canvas.after( 1000, self._what_next) elif self.show['sequence'] == "ordered" and self.show[ 'repeat'] == 'interval' and int( self.show['repeat-interval']) > 0: self._waiting_for_interval = True self._poll_for_interval_timer = self.canvas.after( 1000, self._what_next) elif self.show['sequence'] == "ordered" and self.show[ 'repeat'] == 'interval' and int( self.show['repeat-interval']) == 0: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) else: self.mon.err(self, "Unhandled playing event: ") self._end('error', "Unhandled playing event") else: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) # track has finished and we are on manual progress elif self.show['progress'] == "manual": self._delete_eggtimer() self._display_eggtimer(self.resource('mediashow', 'm03')) self._poll_for_continue_timer = self.canvas.after( 500, self._what_next) else: #unhandled state self.mon.err(self, "Unhandled playing event: ") self._end('error', "Unhandled playing event") def _end_interval_timer(self): self._interval_timer_signal = True # *************************** # Dispatching to Players/Shows # *************************** # used to display internal messages in situations where a medialist entry could be used. def display_message(self, canvas, source, content, duration, _display_message_callback): self._display_message_callback = _display_message_callback tp = { 'duration': duration, 'message-colour': 'white', 'message-font': 'Helvetica 20 bold' } self.player = MessagePlayer(canvas, tp, tp) self.player.play(content, self._display_message_end, None) def _display_message_end(self, reason, message): self.player = None if reason in ('error', 'killed'): self._end(reason, message) else: self._display_message_callback() def complete_path(self, selected_track): # complete path of the filename of the selected entry track_file = selected_track['location'] if track_file[0] == "+": track_file = self.pp_home + track_file[1:] self.mon.log(self, "Track to play is: " + track_file) return track_file def _play_selected_track(self, selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ self.canvas.delete(ALL) if self.show['progress'] == "manual": self._display_eggtimer(self.resource('mediashow', 'm04')) # is menu required if self.show['has-child'] == "yes": enable_child = True else: enable_child = False #dispatch track by type self.player = None self.shower = None track_type = selected_track['type'] self.mon.log(self, "Track type is: " + track_type) if track_type == "video": # create a videoplayer track_file = self.complete_path(selected_track) self.player = VideoPlayer(self.canvas, self.show, selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "image": track_file = self.complete_path(selected_track) # images played from menus don't have children self.player = ImagePlayer(self.canvas, self.show, selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "message": # bit odd because MessagePlayer is used internally to display text. text = selected_track['text'] self.player = MessagePlayer(self.canvas, self.show, selected_track) self.player.play(text, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "show": # get the show from the showlist index = self.showlist.index_of_show(selected_track['sub-show']) if index >= 0: self.showlist.select(index) selected_show = self.showlist.selected_show() else: self.mon.err( self, "Show not found in showlist: " + selected_track['sub-show']) self._end('error', "Unknown show") if selected_show['type'] == "mediashow": self.shower = MediaShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower, top=False, command=self._direction) elif selected_show['type'] == "liveshow": self.shower = LiveShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower, top=False, command='nil') elif selected_show['type'] == "menu": self.shower = MenuShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower, top=False, command='nil') else: self.mon.err(self, "Unknown Show Type: " + selected_show['type']) self._end('error' "Unknown show type") else: self.mon.err(self, "Unknown Track Type: " + track_type) self._end('error', "Unknown track type") def ready_callback(self): self._delete_eggtimer() def end_player(self, reason, message): self._req_next = 'nil' self.mon.log(self, "Returned from player with message: " + message) self.player = None if reason in ("killed", "error"): self._end(reason, message) elif self.show['progress'] == "manual": self._display_eggtimer(self.resource('mediashow', 'm05')) self._req_next = reason self._what_next() else: self._req_next = reason self._what_next() def end_shower(self, reason, message): self._req_next = 'nil' self.mon.log(self, "Returned from shower with message: " + message) self.shower = None if reason in ("killed", "error"): self._end(reason, message) elif self.show['progress'] == "manual": self._display_eggtimer(self.resource('mediashow', 'm06')) self._req_next = reason self._what_next() else: self._req_next = reason self._what_next() def _display_eggtimer(self, text): self.canvas.create_text(int(self.canvas['width']) / 2, int(self.canvas['height']) / 2, text=text, fill='white', font="Helvetica 20 bold") self.canvas.update_idletasks() def _delete_eggtimer(self): self.canvas.delete(ALL)
class MediaShow: # ******************* # External interface # ******************** def __init__(self, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the dictionary fo the show to be played pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory """ self.mon = Monitor() self.mon.on() #instantiate arguments self.show_params = show_params self.showlist = showlist self.root = root self.canvas = canvas self.pp_dir = pp_dir self.pp_home = pp_home self.pp_profile = pp_profile # open resources self.rr = ResourceReader() # Init variables self.player = None self.shower = None self.poll_for_interval_timer = None self.poll_for_continue_timer = None self.waiting_for_interval = False self.interval_timer = None self.duration_timer = None self.error = False self.interval_timer_signal = False self.end_trigger_signal = False self.end_mediashow_signal = False self.next_track_signal = False self.previous_track_signal = False self.play_child_signal = False self.req_next = 'nil' #create and instance of TimeOfDay scheduler so we can add events self.tod = TimeOfDay() self.state = 'closed' def play(self, show_id, end_callback, show_ready_callback, top=False, command='nil'): """ displays the mediashow end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ #instantiate the arguments self.show_id = show_id self.end_callback = end_callback self.show_ready_callback = show_ready_callback self.top = top self.command = command self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Starting show") # check data files are available. self.media_file = self.pp_profile + "/" + self.show_params['medialist'] if not os.path.exists(self.media_file): self.mon.err(self, "Medialist file not found: " + self.media_file) self.end('error', "Medialist file not found") #create a medialist for the mediashow and read it. self.medialist = MediaList() if self.medialist.open_list(self.media_file, self.showlist.sissue()) == False: self.mon.err(self, "Version of medialist different to Pi Presents") self.end('error', "Version of medialist different to Pi Presents") #get controls for this show if top level controlsmanager = ControlsManager() if self.top == True: self.controls_list = controlsmanager.default_controls() # and merge in controls from profile self.controls_list = controlsmanager.merge_show_controls( self.controls_list, self.show_params['controls']) #set up the time of day triggers for the show if self.show_params['trigger'] in ('time', 'time-quiet'): error_text = self.tod.add_times(self.show_params['trigger-input'], id(self), self.tod_start_callback, self.show_params['trigger']) if error_text <> '': self.mon.err(self, error_text) self.end('error', error_text) if self.show_params['trigger-end'] == 'time': # print self.show_params['trigger-end-time'] error_text = self.tod.add_times( self.show_params['trigger-end-time'], id(self), self.tod_end_callback, 'n/a') if error_text <> '': self.mon.err(self, error_text) self.end('error', error_text) if self.show_params['trigger-end'] == 'duration': error_text = self.calculate_duration( self.show_params['trigger-end-time']) if error_text <> '': self.mon.err(self, error_text) self.end('error', error_text) self.state = 'closed' self.egg_timer = None self.wait_for_trigger() # ******************************** # Respond to external events # ******************************** #stop received from another concurrent show def managed_stop(self): # if next lower show is running pass down to stop the show and lower level if self.shower <> None: self.shower.managed_stop() else: #stop the show if not at top self.end_mediashow_signal = True # and if track is runing stop that first if self.player <> None: self.player.input_pressed('stop') # kill or error def terminate(self, reason): if self.shower <> None: self.shower.terminate(reason) elif self.player <> None: self.player.terminate(reason) else: self.end(reason, ' terminated with no shower or player to terminate') # respond to input events def input_pressed(self, symbol, edge, source): self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": received input: " + symbol) # check symbol against mediashow triggers, triggers can be used at top or lower level # and not affected by disable-controls if self.state == 'waiting' and self.show_params['trigger'] in ( 'input', 'input-quiet') and symbol == self.show_params['trigger-input']: self.start_show() elif self.state == 'playing' and self.show_params[ 'trigger-next'] == 'input' and symbol == self.show_params[ 'next-input']: self.next() # internal functions are triggered only when disable-controls is 'no' if self.show_params['disable-controls'] == 'yes': return # if at top convert symbolic name to operation otherwise lower down we have received an operation # look through list of standard symbols to find match (symbolic-name, function name) operation =lookup (symbol if self.top == True: operation = self.lookup_control(symbol, self.controls_list) else: operation = symbol # print 'operation',operation self.do_operation(operation, edge, source) #service the standard inputs for this show def do_operation(self, operation, edge, source): if self.shower <> None: # if next lower show is running pass down to stop the show and lower level self.shower.input_pressed(operation, edge, source) else: # control this show and its tracks # print 'operation',operation if operation == 'stop': if self.top == False: # not at top so stop the current show self.end_mediashow_signal = True # and if a track is running stop that first if self.player <> None: self.player.input_pressed('stop') else: # top = True, just stop track if running if self.player <> None: self.player.input_pressed('stop') elif operation in ('up', 'down'): #if playing rather than waiting use keys for next or previous if operation == 'up' and self.state == 'playing': self.previous() else: self.next() elif operation == 'play': # use 'play' to start child if state=playing or to trigger the show if waiting for trigger if self.state == 'playing': if self.show_params['has-child'] == 'yes': self.play_child_signal = True self.child_track_ref = 'pp-child-show' # and stop the current track if its running if self.player <> None: self.player.input_pressed('stop') else: if self.state == 'waiting': self.start_show() elif operation == 'pause': if self.player <> None: self.player.input_pressed(operation) #if the operation is omxplayer or mplayer runtime control then pass it to player if running elif operation[0:4] == 'omx-' or operation[ 0:6] == 'mplay-' or operation[0:5] == 'uzbl-': if self.player <> None: self.player.input_pressed(operation) def lookup_control(self, symbol, controls_list): for control in controls_list: if symbol == control[0]: return control[1] return '' # *************************** # Show sequencer # *************************** def end_interval_timer(self): self.interval_timer_signal = True # callback from time of day scheduler def tod_start_callback(self): if self.state == 'waiting' and self.show_params['trigger'] in ( 'time', 'time-quiet'): self.start_show() def tod_end_callback(self): if self.state == 'playing' and self.show_params['trigger-end'] in ( 'time', 'duration'): self.end_trigger_signal = True if self.shower <> None: self.shower.input_pressed('stop') elif self.player <> None: self.player.input_pressed('stop') def stop(self, message): self.end_mediashow_signal = True if self.interval_timer <> None: self.canvas.after_cancel(self.interval_timer) def next(self): # stop track if running and set signal self.next_track_signal = True if self.shower <> None: self.shower.input_pressed("stop") else: if self.player <> None: self.player.input_pressed("stop") def previous(self): self.previous_track_signal = True if self.shower <> None: self.shower.input_pressed("stop") else: if self.player <> None: self.player.input_pressed("stop") # wait for trigger sets the state to waiting so that events can do a start show. def wait_for_trigger(self): self.state = 'waiting' if self.show_ready_callback <> None: self.show_ready_callback() self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Waiting for trigger: " + self.show_params['trigger']) if self.show_params['trigger'] == "input": # blank screen waiting for trigger if auto, otherwise display something if self.show_params['progress'] == "manual": text = self.resource('mediashow', 'm01') else: text = self.resource('mediashow', 'm02') self.display_message(self.canvas, 'text', text, 0, self.start_show) elif self.show_params['trigger'] == "input-quiet": # blank screen waiting for trigger text = self.resource('mediashow', 'm10') self.display_message(self.canvas, 'text', text, 0, self.start_show) pass elif self.show_params['trigger'] in ('time', 'time-quiet'): # show next show notice quiet = 3 # if next show is this one display text next_show = self.tod.next_event_time() if next_show[quiet] == False: if next_show[1] == 'tomorrow': text = self.resource('mediashow', 'm09') else: text = self.resource('mediashow', 'm08') text = text.replace('%tt', next_show[0]) self.display_message(self.canvas, 'text', text, 0, self.start_show) elif self.show_params['trigger'] == "start": self.start_show() else: self.mon.err(self, "Unknown trigger: " + self.show_params['trigger']) self.end('error', "Unknown trigger type") def start_show(self): self.state = 'playing' self.direction = 'forward' # self.canvas.delete(ALL) # start interval timer if self.show_params['repeat'] == "interval" and self.show_params[ 'repeat-interval'] <> 0: self.interval_timer_signal = False self.interval_timer = self.canvas.after( int(self.show_params['repeat-interval']) * 1000, self.end_interval_timer) # start duration timer if self.show_params['trigger-end'] == 'duration': # print 'set alarm ', self.duration self.duration_timer = self.canvas.after(self.duration * 1000, self.tod_end_callback) # and play the first track unless commanded otherwise if self.command == 'backward': self.medialist.finish() else: self.medialist.start() self.play_selected_track(self.medialist.selected_track()) def what_next(self): self.direction = 'forward' # end of show trigger caused by tod if self.end_trigger_signal == True: self.end_trigger_signal = False if self.top == True: self.state = 'waiting' self.wait_for_trigger() else: # not at top so stop the show self.end('normal', 'sub-show end time trigger') # user wants to end, wait for any shows or tracks to have ended then end show # probalby will get here with end_m set when player and shower has finished elif self.end_mediashow_signal == True: if self.player == None and self.shower == None: self.end_mediashow_signal = False self.end('normal', "show ended by user") #returning from a subshow needing to move onward elif self.req_next == 'do-next': self.req_next = 'nil' self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) #returning from a subshow needing to move backward elif self.req_next == 'do-previous': self.req_next = 'nil' self.direction = 'backward' self.medialist.previous(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # user wants to play child elif self.play_child_signal == True: self.play_child_signal = False index = self.medialist.index_of_track(self.child_track_ref) if index >= 0: #don't use select the track as need to preserve mediashow sequence. child_track = self.medialist.track(index) self.display_eggtimer(self.resource('mediashow', 'm07')) self.play_selected_track(child_track) else: self.mon.err( self, "Child show not found in medialist: " + self.show_params['pp-child-show']) self.end('error', "child show not found in medialist") # skip to next track on user input elif self.next_track_signal == True: self.next_track_signal = False if self.medialist.at_end() == True: if self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'oneshot' and self.top == False: self.end('do-next', "Return from Sub Show") elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'single-run' and self.top == False: self.end('do-next', "Return from Sub Show") else: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) else: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # skip to previous track on user input elif self.previous_track_signal == True: self.previous_track_signal = False self.direction = 'backward' if self.medialist.at_start() == True: if self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'oneshot' and self.top == False: self.end('do-previous', "Return from Sub Show") elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'single-run' and self.top == False: self.end('do-previous', "Return from Sub Show") else: self.medialist.previous(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) else: self.medialist.previous(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # track is finished and we are on auto elif self.show_params['progress'] == "auto": if self.medialist.at_end() == True: # oneshot if self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'oneshot' and self.top == False: self.end('normal', "End of Oneshot in subshow") elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'oneshot' and self.top == True: self.wait_for_trigger() # single run elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'single-run' and self.top == True: self.end('normal', "End of Single Run") elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'single-run' and self.top == False: self.end('do-next', "End of single run - Return from Sub Show") # repeating and waiting to restart elif self.waiting_for_interval == True: if self.interval_timer_signal == True: self.interval_timer_signal = False self.waiting_for_interval = False self.start_show() else: self.poll_for_interval_timer = self.canvas.after( 1000, self.what_next) elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'interval' and int( self.show_params['repeat-interval']) > 0: self.waiting_for_interval = True self.poll_for_interval_timer = self.canvas.after( 1000, self.what_next) #elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])==0: elif self.show_params['repeat'] == 'interval' and int( self.show_params['repeat-interval']) == 0: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # shuffling so there is no end condition elif self.show_params['sequence'] == "shuffle": self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) else: self.mon.err( self, "Unhandled playing event: " + self.show_params['sequence'] + ' with ' + self.show_params['repeat'] + " of " + self.show_params['repeat-interval']) self.end('error', "Unhandled playing event") else: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # track has finished and we are on manual progress elif self.show_params['progress'] == "manual": self.delete_eggtimer() self.canvas.delete('pp-content') if self.show_params['trigger-next'] == 'input': self.display_eggtimer(self.resource('mediashow', 'm03')) self.poll_for_continue_timer = self.canvas.after( 2000, self.what_next) else: #unhandled state self.mon.err(self, "Unhandled playing event: ") self.end('error', "Unhandled playing event") # *************************** # Dispatching to Players/Shows # *************************** def ready_callback(self): self.delete_eggtimer() def play_selected_track(self, selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ self.delete_eggtimer() if self.show_params['progress'] == "manual": self.display_eggtimer(self.resource('mediashow', 'm04')) # is menu required if self.show_params['has-child'] == "yes": self.enable_child = True else: self.enable_child = False #dispatch track by type self.player = None self.shower = None track_type = selected_track['type'] self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Track type is: " + track_type) if track_type == "video": # create a videoplayer track_file = self.complete_path(selected_track) self.player = VideoPlayer(self.show_id, self.root, self.canvas, self.show_params, selected_track, self.pp_dir, self.pp_home, self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type == "audio": # create a audioplayer track_file = self.complete_path(selected_track) self.player = AudioPlayer(self.show_id, self.root, self.canvas, self.show_params, selected_track, self.pp_dir, self.pp_home, self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type == "web": # create a browser track_file = self.complete_path(selected_track) self.player = BrowserPlayer(self.show_id, self.root, self.canvas, self.show_params, selected_track, self.pp_dir, self.pp_home, self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type == "image": track_file = self.complete_path(selected_track) # images played from menus don't have children self.player = ImagePlayer(self.show_id, self.root, self.canvas, self.show_params, selected_track, self.pp_dir, self.pp_home, self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type == "message": # bit odd because MessagePlayer is used internally to display text. text = selected_track['text'] self.player = MessagePlayer(self.show_id, self.root, self.canvas, self.show_params, selected_track, self.pp_dir, self.pp_home, self.pp_profile) self.player.play(text, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type == "show": # get the show from the showlist index = self.showlist.index_of_show(selected_track['sub-show']) if index >= 0: self.showlist.select(index) selected_show = self.showlist.selected_show() else: self.mon.err( self, "Show not found in showlist: " + selected_track['sub-show']) self.end('error', "Unknown show") if selected_show['type'] == "mediashow": self.shower = MediaShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, self.ready_callback, top=False, command=self.direction) elif selected_show['type'] == "liveshow": self.shower = LiveShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, self.ready_callback, top=False, command='nil') elif selected_show['type'] == "radiobuttonshow": self.shower = RadioButtonShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, self.ready_callback, top=False, command='nil') elif selected_show['type'] == "hyperlinkshow": self.shower = HyperlinkShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, self.ready_callback, top=False, command='nil') elif selected_show['type'] == "menu": self.shower = MenuShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, self.ready_callback, top=False, command='nil') else: self.mon.err(self, "Unknown Show Type: " + selected_show['type']) self.end('error' "Unknown show type") else: self.mon.err(self, "Unknown Track Type: " + track_type) self.end('error', "Unknown track type") def end_player(self, reason, message): self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Returned from player with message: " + message) self.player = None self.req_next = 'nil' if reason in ("killed", "error"): self.end(reason, message) else: # elif>else move to what-next? if self.show_params['progress'] == "manual": self.display_eggtimer(self.resource('mediashow', 'm05')) self.req_next = reason self.what_next() else: self.req_next = reason self.what_next() def end_shower(self, show_id, reason, message): self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Returned from shower with message: " + message) self.shower = None self.req_next = 'nil' if reason in ("killed", "error"): self.end(reason, message) else: if self.show_params['progress'] == "manual": self.display_eggtimer(self.resource('mediashow', 'm06')) self.req_next = reason self.what_next() else: self.req_next = reason self.what_next() # *************************** # end of show # *************************** def end(self, reason, message): self.end_mediashow_signal = False self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Ending Mediashow") self.tidy_up() self.end_callback(self.show_id, reason, message) self = None return def tidy_up(self): #clear outstanding time of day events for this show # self.tod.clear_times_list(id(self)) if self.poll_for_continue_timer <> None: self.canvas.after_cancel(self.poll_for_continue_timer) self.poll_for_continue_timer = None if self.poll_for_interval_timer <> None: self.canvas.after_cancel(self.poll_for_interval_timer) self.poll_for_interval_timer = None if self.interval_timer <> None: self.canvas.after_cancel(self.interval_timer) self.interval_timer = None if self.duration_timer <> None: self.canvas.after_cancel(self.duration_timer) self.duration_timer = None # *************************** # displaying things # *************************** def display_eggtimer(self, text): self.canvas.create_text(int(self.canvas['width']) / 2, int(self.canvas['height']) / 2, text=text, fill='white', font="Helvetica 20 bold", tag='pp-eggtimer') self.canvas.update_idletasks() def delete_eggtimer(self): self.canvas.delete('pp-eggtimer') self.canvas.update_idletasks() # used to display internal messages in situations where a medialist entry could not be used. def display_message(self, canvas, source, content, duration, _display_message_callback): self.display_message_callback = _display_message_callback tp = { 'duration': duration, 'message-colour': 'white', 'message-font': 'Helvetica 20 bold', 'background-colour': '', 'message-justify': 'left', 'background-image': '', 'show-control-begin': '', 'show-control-end': '', 'animate-begin': '', 'animate-clear': '', 'animate-end': '', 'message-x': '', 'message-y': '', 'display-show-background': 'no', 'display-show-text': 'no', 'show-text': '', 'track-text': '', 'plugin': '' } self.player = MessagePlayer(self.show_id, self.root, canvas, tp, tp, self.pp_dir, self.pp_home, self.pp_profile) self.player.play(content, self.showlist, self.display_message_end, None, False) def display_message_end(self, reason, message): self.player = None if reason in ('error', 'killed'): self.end(reason, message) else: self.display_message_callback() # *************************** # utilities # *************************** def calculate_duration(self, line): fields = line.split(':') if len(fields) == 1: secs = fields[0] minutes = '0' hours = '0' if len(fields) == 2: secs = fields[1] minutes = fields[0] hours = '0' if len(fields) == 3: secs = fields[2] minutes = fields[1] hours = fields[0] self.duration = 3600 * long(hours) + 60 * long(minutes) + long(secs) return '' def resource(self, section, item): value = self.rr.get(section, item) if value == False: self.mon.err(self, "resource: " + section + ': ' + item + " not found") self.terminate("error") else: return value def complete_path(self, selected_track): # complete path of the filename of the selected entry track_file = selected_track['location'] if track_file <> '' and track_file[0] == "+": track_file = self.pp_home + track_file[1:] self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Track to play is: " + track_file) return track_file
class MediaShow: # ******************* # External interface # ******************** def __init__(self, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the dictionary fo the show to be played pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory """ self.mon=Monitor() self.mon.on() #instantiate arguments self.show_params =show_params self.showlist=showlist self.root=root self.canvas=canvas self.pp_dir=pp_dir self.pp_home=pp_home self.pp_profile=pp_profile # open resources self.rr=ResourceReader() # Init variables self.player=None self.shower=None self.poll_for_interval_timer=None self.poll_for_continue_timer=None self.waiting_for_interval=False self.interval_timer=None self.duration_timer=None self.error=False self.interval_timer_signal=False self.end_trigger_signal=False self.end_mediashow_signal=False self.next_track_signal=False self.previous_track_signal=False self.play_child_signal = False self.req_next='nil' #create and instance of TimeOfDay scheduler so we can add events self.tod=TimeOfDay() self.state='closed' def play(self,show_id,end_callback,show_ready_callback, top=False,command='nil'): """ displays the mediashow end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ #instantiate the arguments self.show_id=show_id self.end_callback=end_callback self.show_ready_callback=show_ready_callback self.top=top self.command=command self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Starting show") # check data files are available. self.media_file = self.pp_profile + "/" + self.show_params['medialist'] if not os.path.exists(self.media_file): self.mon.err(self,"Medialist file not found: "+ self.media_file) self.end('error',"Medialist file not found") #create a medialist for the mediashow and read it. self.medialist=MediaList(self.show_params['sequence']) if self.medialist.open_list(self.media_file,self.showlist.sissue())==False: self.mon.err(self,"Version of medialist different to Pi Presents") self.end('error',"Version of medialist different to Pi Presents") #get controls for this show if top level controlsmanager=ControlsManager() if self.top==True: self.controls_list=controlsmanager.default_controls() # and merge in controls from profile self.controls_list=controlsmanager.merge_show_controls(self.controls_list,self.show_params['controls']) #set up the time of day triggers for the show if self.show_params['trigger']in('time','time-quiet'): error_text=self.tod.add_times(self.show_params['trigger-input'],id(self),self.tod_start_callback,self.show_params['trigger']) if error_text<>'': self.mon.err(self,error_text) self.end('error',error_text) if self.show_params['trigger-end']=='time': # print self.show_params['trigger-end-time'] error_text=self.tod.add_times(self.show_params['trigger-end-time'],id(self),self.tod_end_callback,'n/a') if error_text<>'': self.mon.err(self,error_text) self.end('error',error_text) if self.show_params['trigger-end']=='duration': error_text=self.calculate_duration(self.show_params['trigger-end-time']) if error_text<>'': self.mon.err(self,error_text) self.end('error',error_text) self.state='closed' self.egg_timer=None self.wait_for_trigger() # ******************************** # Respond to external events # ******************************** #stop received from another concurrent show def managed_stop(self): # if next lower show is running pass down to stop the show and lower level if self.shower<>None: self.shower.managed_stop() else: #stop the show if not at top self.end_mediashow_signal=True # and if track is runing stop that first if self.player<>None: self.player.input_pressed('stop') # kill or error def terminate(self,reason): if self.shower<>None: self.shower.terminate(reason) elif self.player<>None: self.player.terminate(reason) else: self.end(reason,' terminated with no shower or player to terminate') # respond to input events def input_pressed(self,symbol,edge,source): self.mon.log(self, self.show_params['show-ref']+ ' '+ str(self.show_id)+": received input: " + symbol) # check symbol against mediashow triggers, triggers can be used at top or lower level # and not affected by disable-controls if self.state=='waiting' and self.show_params['trigger'] in ('input','input-quiet')and symbol == self.show_params['trigger-input']: self.start_show() elif self.state=='playing' and self.show_params['trigger-next']=='input' and symbol == self.show_params['next-input']: self.next() # internal functions are triggered only when disable-controls is 'no' if self.show_params['disable-controls']=='yes': return # if at top convert symbolic name to operation otherwise lower down we have received an operation # look through list of standard symbols to find match (symbolic-name, function name) operation =lookup (symbol if self.top==True: operation=self.lookup_control(symbol,self.controls_list) else: operation=symbol # print 'operation',operation self.do_operation(operation,edge,source) #service the standard inputs for this show def do_operation(self,operation,edge,source): if self.shower<>None: # if next lower show is running pass down to stop the show and lower level self.shower.input_pressed(operation,edge,source) else: # control this show and its tracks # print 'operation',operation if operation=='stop': if self.top == False: # not at top so stop the current show self.end_mediashow_signal=True # and if a track is running stop that first if self.player<>None: self.player.input_pressed('stop') else: # top = True, just stop track if running if self.player<>None: self.player.input_pressed('stop') elif operation in ('up','down'): #if playing rather than waiting use keys for next or previous if operation=='up' and self.state=='playing': self.previous() else: self.next() elif operation=='play': # use 'play' to start child if state=playing or to trigger the show if waiting for trigger if self.state=='playing': if self.show_params['has-child']=='yes': self.play_child_signal=True self.child_track_ref='pp-child-show' # and stop the current track if its running if self.player<>None: self.player.input_pressed('stop') else: if self.state=='waiting': self.start_show() elif operation == 'pause': if self.player<>None: self.player.input_pressed(operation) #if the operation is omxplayer or mplayer runtime control then pass it to player if running elif operation[0:4]=='omx-' or operation[0:6]=='mplay-'or operation[0:5]=='uzbl-': if self.player<>None: self.player.input_pressed(operation) def lookup_control(self,symbol,controls_list): for control in controls_list: if symbol == control[0]: return control[1] return '' # *************************** # Show sequencer # *************************** def end_interval_timer(self): self.interval_timer_signal=True # callback from time of day scheduler def tod_start_callback(self): if self.state=='waiting' and self.show_params['trigger']in('time','time-quiet'): self.start_show() def tod_end_callback(self): if self.state=='playing' and self.show_params['trigger-end'] in ('time','duration'): self.end_trigger_signal=True if self.shower<>None: self.shower.input_pressed('stop') elif self.player<>None: self.player.input_pressed('stop') def stop(self,message): self.end_mediashow_signal=True if self.interval_timer<>None: self.canvas.after_cancel(self.interval_timer) def next(self): # stop track if running and set signal self.next_track_signal=True if self.shower<>None: self.shower.input_pressed("stop") else: if self.player<>None: self.player.input_pressed("stop") def previous(self): self.previous_track_signal=True if self.shower<>None: self.shower.input_pressed("stop") else: if self.player<>None: self.player.input_pressed("stop") # wait for trigger sets the state to waiting so that events can do a start show. def wait_for_trigger(self): self.state='waiting' if self.show_ready_callback<>None: self.show_ready_callback() self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Waiting for trigger: "+ self.show_params['trigger']) if self.show_params['trigger']=="input": # blank screen waiting for trigger if auto, otherwise display something if self.show_params['progress']=="manual": text= self.resource('mediashow','m01') else: text= self.resource('mediashow','m02') self.display_message(self.canvas,'text',text,0,self.start_show) elif self.show_params['trigger']=="input-quiet": # blank screen waiting for trigger text = self.resource('mediashow','m10') self.display_message(self.canvas,'text',text,0,self.start_show) pass elif self.show_params['trigger'] in ('time','time-quiet'): # show next show notice quiet=3 # if next show is this one display text next_show=self.tod.next_event_time() if next_show[quiet]==False: if next_show[1]=='tomorrow': text = self.resource('mediashow','m09') else: text = self.resource('mediashow','m08') text=text.replace('%tt',next_show[0]) self.display_message(self.canvas,'text',text,0,self.start_show) elif self.show_params['trigger']=="start": self.start_show() else: self.mon.err(self,"Unknown trigger: "+ self.show_params['trigger']) self.end('error',"Unknown trigger type") def start_show(self): self.state='playing' self.direction='forward' # self.canvas.delete(ALL) # start interval timer if self.show_params['repeat']=="interval" and self.show_params['repeat-interval']<>0: self.interval_timer_signal=False self.interval_timer=self.canvas.after(int(self.show_params['repeat-interval'])*1000,self.end_interval_timer) # start duration timer if self.show_params['trigger-end']=='duration': # print 'set alarm ', self.duration self.duration_timer = self.canvas.after(self.duration*1000,self.tod_end_callback) # and play the first track unless commanded otherwise if self.command=='backward': self.medialist.finish() else: self.medialist.start() self.play_selected_track(self.medialist.selected_track()) def what_next(self): self.direction='forward' # end of show trigger caused by tod if self.end_trigger_signal==True: self.end_trigger_signal=False if self.top==True: self.state='waiting' self.wait_for_trigger() else: # not at top so stop the show self.end('normal','sub-show end time trigger') # user wants to end, wait for any shows or tracks to have ended then end show # probalby will get here with end_m set when player and shower has finished elif self.end_mediashow_signal==True: if self.player==None and self.shower==None: self.end_mediashow_signal=False self.end('normal',"show ended by user") #returning from a subshow needing to move onward elif self.req_next=='do-next': self.req_next='nil' self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) #returning from a subshow needing to move backward elif self.req_next=='do-previous': self.req_next='nil' self.direction='backward' self.medialist.previous(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # user wants to play child elif self.play_child_signal == True: self.play_child_signal=False index = self.medialist.index_of_track(self.child_track_ref) if index >=0: #don't use select the track as need to preserve mediashow sequence. child_track=self.medialist.track(index) self.display_eggtimer(self.resource('mediashow','m07')) self.play_selected_track(child_track) else: self.mon.err(self,"Child show not found in medialist: "+ self.show_params['pp-child-show']) self.end('error',"child show not found in medialist") # skip to next track on user input elif self.next_track_signal==True: self.next_track_signal=False if self.medialist.at_end()==True: if self.show_params['sequence']=="ordered" and self.show_params['repeat']=='oneshot' and self.top==False: self.end('do-next',"Return from Sub Show") elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='single-run' and self.top==False: self.end('do-next',"Return from Sub Show") else: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) else: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # skip to previous track on user input elif self.previous_track_signal==True: self.previous_track_signal=False self.direction='backward' if self.medialist.at_start()==True: if self.show_params['sequence']=="ordered" and self.show_params['repeat']=='oneshot' and self.top==False: self.end('do-previous',"Return from Sub Show") elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='single-run' and self.top==False: self.end('do-previous',"Return from Sub Show") else: self.medialist.previous(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) else: self.medialist.previous(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # track is finished and we are on auto elif self.show_params['progress']=="auto": if self.medialist.at_end()==True: # oneshot if self.show_params['repeat']=='oneshot' and self.top==False: self.end('normal',"End of Oneshot in subshow") elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='oneshot' and self.top==True: self.wait_for_trigger() # single run elif self.show_params['repeat']=='single-run' and self.top==True: self.end('normal',"End of Single Run") elif self.show_params['repeat']=='single-run' and self.top==False: self.end('do-next',"End of single run - Return from Sub Show") # repeating and waiting to restart elif self.waiting_for_interval==True: if self.interval_timer_signal==True: self.interval_timer_signal=False self.waiting_for_interval=False self.start_show() else: self.poll_for_interval_timer=self.canvas.after(1000,self.what_next) elif self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])>0: self.waiting_for_interval=True self.poll_for_interval_timer=self.canvas.after(1000,self.what_next) #elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])==0: elif self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])==0: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # shuffling so there is no end condition elif self.show_params['sequence']=="shuffle": self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) else: self.mon.err(self,"Unhandled playing event: "+self.show_params['sequence'] +' with ' + self.show_params['repeat']+" of "+ self.show_params['repeat-interval']) self.end('error',"Unhandled playing event") else: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # track has finished and we are on manual progress elif self.show_params['progress']=="manual": self.delete_eggtimer() self.canvas.delete('pp-content') if self.show_params['trigger-next']=='input': self.display_eggtimer(self.resource('mediashow','m03')) self.poll_for_continue_timer=self.canvas.after(2000,self.what_next) else: #unhandled state self.mon.err(self,"Unhandled playing event: ") self.end('error',"Unhandled playing event") # *************************** # Dispatching to Players/Shows # *************************** def ready_callback(self): self.delete_eggtimer() def play_selected_track(self,selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ self.delete_eggtimer() if self.show_params['progress']=="manual": self.display_eggtimer(self.resource('mediashow','m04')) # is menu required if self.show_params['has-child']=="yes": self.enable_child=True else: self.enable_child=False #dispatch track by type self.player=None self.shower=None track_type = selected_track['type'] self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Track type is: "+ track_type) if track_type=="video": # create a videoplayer track_file=self.complete_path(selected_track) self.player=VideoPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type=="audio": # create a audioplayer track_file=self.complete_path(selected_track) self.player=AudioPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type=="web": # create a browser track_file=self.complete_path(selected_track) self.player=BrowserPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type=="image": track_file=self.complete_path(selected_track) # images played from menus don't have children self.player=ImagePlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type=="message": # bit odd because MessagePlayer is used internally to display text. text=selected_track['text'] self.player=MessagePlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(text, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child ) elif track_type=="show": # get the show from the showlist index = self.showlist.index_of_show(selected_track['sub-show']) if index >=0: self.showlist.select(index) selected_show=self.showlist.selected_show() else: self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show']) self.end('error',"Unknown show") if selected_show['type']=="mediashow": self.shower= MediaShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) print "Starting MediaShow: {0}".format(selected_track['sub-show']) self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command=self.direction) elif selected_show['type']=="liveshow": self.shower= LiveShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil') elif selected_show['type']=="radiobuttonshow": self.shower= RadioButtonShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil') elif selected_show['type']=="hyperlinkshow": self.shower= HyperlinkShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil') elif selected_show['type']=="menu": self.shower= MenuShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil') else: self.mon.err(self,"Unknown Show Type: "+ selected_show['type']) self.end('error'"Unknown show type") else: self.mon.err(self,"Unknown Track Type: "+ track_type) self.end('error',"Unknown track type") def end_player(self,reason,message): self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Returned from player with message: "+ message) self.player=None self.req_next='nil' if reason in("killed","error"): self.end(reason,message) else: # elif>else move to what-next? if self.show_params['progress']=="manual": self.display_eggtimer(self.resource('mediashow','m05')) self.req_next=reason self.what_next() else: self.req_next=reason self.what_next() def end_shower(self,show_id,reason,message): self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Returned from shower with message: "+ message) self.shower=None self.req_next='nil' if reason in("killed","error"): self.end(reason,message) else: if self.show_params['progress']=="manual": self.display_eggtimer(self.resource('mediashow','m06')) self.req_next=reason self.what_next() else: self.req_next=reason self.what_next() # *************************** # end of show # *************************** def end(self,reason,message): self.end_mediashow_signal=False self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Ending Mediashow") self.tidy_up() self.end_callback(self.show_id,reason,message) self=None return def tidy_up(self): #clear outstanding time of day events for this show # self.tod.clear_times_list(id(self)) if self.poll_for_continue_timer<>None: self.canvas.after_cancel(self.poll_for_continue_timer) self.poll_for_continue_timer=None if self.poll_for_interval_timer<>None: self.canvas.after_cancel(self.poll_for_interval_timer) self.poll_for_interval_timer=None if self.interval_timer<>None: self.canvas.after_cancel(self.interval_timer) self.interval_timer=None if self.duration_timer<>None: self.canvas.after_cancel(self.duration_timer) self.duration_timer=None # *************************** # displaying things # *************************** def display_eggtimer(self,text): self.canvas.create_text(int(self.canvas['width'])/2, int(self.canvas['height'])/2, text= text, fill='white', font="Helvetica 20 bold", tag='pp-eggtimer') self.canvas.update_idletasks( ) def delete_eggtimer(self): self.canvas.delete('pp-eggtimer') self.canvas.update_idletasks( ) # used to display internal messages in situations where a medialist entry could not be used. def display_message(self,canvas,source,content,duration,_display_message_callback): self.display_message_callback=_display_message_callback tp={'duration':duration,'message-colour':'white','message-font':'Helvetica 20 bold','background-colour':'', 'message-justify':'left','background-image':'','show-control-begin':'','show-control-end':'', 'animate-begin':'','animate-clear':'','animate-end':'','message-x':'','message-y':'', 'display-show-background':'no','display-show-text':'no','show-text':'','track-text':'', 'plugin':''} self.player=MessagePlayer(self.show_id,self.root,canvas,tp,tp,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(content,self.showlist,self.display_message_end,None,False) def display_message_end(self,reason,message): self.player=None if reason in ('error','killed'): self.end(reason,message) else: self.display_message_callback() # *************************** # utilities # *************************** def calculate_duration(self,line): fields=line.split(':') if len(fields)==1: secs=fields[0] minutes='0' hours='0' if len(fields)==2: secs=fields[1] minutes=fields[0] hours='0' if len(fields)==3: secs=fields[2] minutes=fields[1] hours=fields[0] self.duration=3600*long(hours)+60*long(minutes)+long(secs) return '' def resource(self,section,item): value=self.rr.get(section,item) if value==False: self.mon.err(self, "resource: "+section +': '+ item + " not found" ) self.terminate("error") else: return value def complete_path(self,selected_track): # complete path of the filename of the selected entry track_file = selected_track['location'] if track_file<>'' and track_file[0]=="+": track_file=self.pp_home+track_file[1:] self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Track to play is: "+ track_file) return track_file
class MenuShow: """ Displays a menu with optional hint below it. User can traverse the menu and select a track using key or button presses. Interface: * play - displays the menu and selects the first entry * input_pressed, - receives user events passes them to a Player if a track is playing, otherwise actions them with _next, _previous, _play_selected_track, _end Optional display of eggtimer by means of Players ready_callback Supports imageplayer, videoplayer,messagplayer,audioplayer,menushow,mediashow Destroys itself on exit """ # ********************* # external interface # ******************** def __init__(self, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the name of the configuration dictionary section for the menu showlist - the showlist pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory""" self.mon=Monitor() self.mon.on() self.display_guidelines_command=show_params['menu-guidelines'] self.display_guidelines=self.display_guidelines_command #instantiate arguments self.show_params=show_params self.root=root self.canvas=canvas self.showlist=showlist self.pp_dir=pp_dir self.pp_home=pp_home self.pp_profile=pp_profile # open resources self.rr=ResourceReader() # init variables self.drawn = None self.player=None self.shower=None self.menu_timeout_running=None self.error=False def play(self,show_id,end_callback,ready_callback,top=False,command='nil'): """ displays the menu end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ #instantiate arguments self.show_id=show_id self.end_callback=end_callback self.ready_callback=ready_callback self.top=top self.command=command # check data files are available. self.menu_file = self.pp_profile + "/" + self.show_params['medialist'] if not os.path.exists(self.menu_file): self.mon.err(self,"Medialist file not found: "+ self.menu_file) self.end('error',"Medialist file not found") #create a medialist for the menu and read it. self.medialist=MediaList() if self.medialist.open_list(self.menu_file,self.showlist.sissue()) == False: self.mon.err(self,"Version of medialist different to Pi Presents") self.end('error',"Version of medialist different to Pi Presents") #get control bindings for this show if top level controlsmanager=ControlsManager() if self.top==True: self.controls_list=controlsmanager.default_controls() # and merge in controls from profile self.controls_list=controlsmanager.merge_show_controls(self.controls_list,self.show_params['controls']) if self.show_params['has-background']=="yes": background_index=self.medialist.index_of_track ('pp-menu-background') if background_index>=0: self.menu_img_file = self.complete_path(self.medialist.track(background_index)['location']) if not os.path.exists(self.menu_img_file): self.mon.err(self,"Menu background file not found: "+ self.menu_img_file) self.end('error',"Menu background file not found") else: self.mon.err(self,"Menu background not found in medialist") self.end('error',"Menu background not found") self.end_menushow_signal= False if self.ready_callback<>None: self.ready_callback() self.menu_timeout_value=int(self.show_params['timeout'])*1000 self.do_menu() def do_menu(self): #start timeout alarm if required if int(self.show_params['timeout'])<>0: self.menu_timeout_running=self.canvas.after(self.menu_timeout_value,self.timeout_menu) if self.show_params['menu-background-colour']<>'': self.canvas.config(bg=self.show_params['menu-background-colour']) self.canvas.delete('pp-content') self.canvas.update() # display background image if self.show_params['has-background']=="yes": self.display_background() self.delete_eggtimer() self.display_new_menu() self.canvas.tag_raise('pp-click-area') self.canvas.update_idletasks( ) # display menu text if enabled if self.show_params['menu-text']<> '': self.canvas.create_text(int(self.show_params['menu-text-x']),int(self.show_params['menu-text-y']), anchor=NW, text=self.show_params['menu-text'], fill=self.show_params['menu-text-colour'], font=self.show_params['menu-text-font'], tag='pp-content') self.canvas.update_idletasks( ) # display instructions (hint) hint_text=self.show_params['hint-text'] if hint_text<>'': self.canvas.create_text(int(self.show_params['hint-x']),int(self.show_params['hint-y']), anchor=NW, text=hint_text, fill=self.show_params['hint-colour'], font=self.show_params['hint-font'], tag='pp-content') self.canvas.update_idletasks( ) #stop received from another concurrent show def managed_stop(self): if self.menu_timeout_running<>None: self.canvas.after_cancel(self.menu_timeout_running) self.menu_timeout_running=None if self.shower<>None: self.shower.managed_stop() elif self.player<>None: self.end_menushow_signal=True self.player.input_pressed('stop') else: self.end('normal','stopped by ShowManager') # kill or error received def terminate(self,reason): if self.menu_timeout_running<>None: self.canvas.after_cancel(self.menu_timeout_running) self.menu_timeout_running=None if self.shower<>None: self.shower.terminate(reason) elif self.player<>None: self.player.terminate(reason) else: self.end(reason,'Terminated no shower or player running') # respond to user inputs. def input_pressed(self,symbol,edge,source): self.mon.log(self,"Show Id: "+str(self.show_id)+" received key or operation: " + symbol) if self.show_params['disable-controls']=='yes': return # if at top convert symbolic name to operation otherwise lower down we have received an operation # look through list of standard symbols to find match (symbolic-name, function name) operation =lookup (symbol if self.top==True: operation=self.lookup_control(symbol,self.controls_list) else: operation=symbol # print 'operation',operation # if no match for symbol against standard operations then return if operation=='': return else: if self.shower<>None: # if next lower show is running pass down operatin to the show and lower levels self.shower.input_pressed(operation,source,edge) else: #service the standard inputs for this show if operation=='stop': if self.menu_timeout_running<>None: self.canvas.after_cancel(self.menu_timeout_running) self.menu_timeout_running=None if self.shower<>None: self.shower.input_pressed('stop',edge,source) elif self.player<>None: self.player.input_pressed('stop') else: # not at top so end the show if self.top == False: self.end('normal',"exit from stop command") else: pass elif operation in ('up','down'): # if child or sub-show running and is a show pass down # if child not running - move if self.shower<>None: self.shower.input_pressed(operation,edge,source) else: if self.player==None: if self.menu_timeout_running<>None: self.canvas.after_cancel(self.menu_timeout_running) self.menu_timeout_running=self.canvas.after(self.menu_timeout_value,self.timeout_menu) if operation=='up': self.previous() else: self.next() elif operation =='play': # if child running and is show - pass down # if no track already running - play if self.shower<>None: self.shower.input_pressed(operation,edge,source) else: if self.player==None: self.play_selected_track(self.medialist.selected_track()) elif operation == 'pause': # pass down if show or track running. if self.shower<>None: self.shower.input_pressed(operation,edge,source) elif self.player<>None: self.player.input_pressed(operation) elif operation[0:4]=='omx-' or operation[0:6]=='mplay-'or operation[0:5]=='uzbl-': if self.player<>None: self.player.input_pressed(operation) def lookup_control(self,symbol,controls_list): for control in controls_list: if symbol == control[0]: return control[1] return '' # ********************* # INTERNAL FUNCTIONS # ******************** # ********************* # Sequencing # ********************* def timeout_menu(self): self.end('normal','menu timeout') return def next(self): self.highlight_menu_entry(self.menu_index,False) self.medialist.next('ordered') if self.menu_index==self.menu_length-1: self.menu_index=0 else: self.menu_index+=1 self.highlight_menu_entry(self.menu_index,True) def previous(self): self.highlight_menu_entry(self.menu_index,False) if self.menu_index==0: self.menu_index=self.menu_length-1 else: self.menu_index-=1 self.medialist.previous('ordered') self.highlight_menu_entry(self.menu_index,True) # at the end of a track just re-display the menu with the original callback from the menu def what_next(self,message): # user wants to end if self.end_menushow_signal==True: self.end_menushow_signal=False self.end('normal',"show ended by user") else: self.do_menu() # ********************* # Dispatching to Players # ********************* def play_selected_track(self,selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ #remove menu and show working..... if self.menu_timeout_running<>None: self.canvas.after_cancel(self.menu_timeout_running) self.menu_timeout_running=None self.canvas.delete('pp-content') self.display_eggtimer(self.resource('menushow','m01')) # dispatch track by type self.player=None self.shower=None track_type = selected_track['type'] self.mon.log(self,"Track type is: "+ track_type) if track_type=="video": # create a videoplayer track_file=self.complete_path(selected_track['location']) self.player=VideoPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.delete_eggtimer, enable_menu=False) elif track_type=="audio": # create a audioplayer track_file=self.complete_path(selected_track['location']) self.player=AudioPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.delete_eggtimer, enable_menu=False) elif track_type=="image": # images played from menus don't have children track_file=self.complete_path(selected_track['location']) self.player=ImagePlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.delete_eggtimer, enable_menu=False, ) elif track_type=="web": # create a browser track_file=self.complete_path(selected_track['location']) self.player=BrowserPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.delete_eggtimer, enable_menu=False) elif track_type=="message": # bit odd because MessagePlayer is used internally to display text. text=selected_track['text'] self.player=MessagePlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(text, self.showlist, self.end_player, self.delete_eggtimer, enable_menu=False ) elif track_type=="show": # get the show from the showlist index = self.showlist.index_of_show(selected_track['sub-show']) if index >=0: self.showlist.select(index) selected_show=self.showlist.selected_show() else: self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show']) self.end("Unknown show") if selected_show['type']=="mediashow": self.shower= MediaShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.delete_eggtimer,top=False,command='nil') elif selected_show['type']=="liveshow": self.shower= LiveShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.delete_eggtimer,top=False,command='nil') elif selected_show['type']=="radiobuttonshow": self.shower= RadioButtonShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil') elif selected_show['type']=="hyperlinkshow": self.shower= HyperlinkShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil') elif selected_show['type']=="menu": self.shower= MenuShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.delete_eggtimer,top=False,command='nil') else: self.mon.err(self,"Unknown Show Type: "+ selected_show['type']) self.end("Unknown show type") else: self.mon.err(self,"Unknown Track Type: "+ track_type) self.end("Unknown track type") # callback from when player ends def end_player(self,reason,message): self.mon.log(self,"Returned from player with message: "+ message) self.player=None if reason in("killed","error"): self.end(reason,message) else: self.display_eggtimer(self.resource('menushow','m02')) self.what_next(message) # callback from when shower ends def end_shower(self,show_id,reason,message): self.mon.log(self,"Returned from shower with message: "+ message) self.shower=None if reason in ("killed","error"): self.end(reason,message) else: self.display_eggtimer(self.resource('menushow','m03')) self.what_next(message) # ********************* # Ending the show # ********************* # finish the player for killing, error or normally # this may be called directly if sub/child shows or players are not running # if they might be running then need to call terminate????? def end(self,reason,message): self.mon.log(self,"Ending menushow: "+ self.show_params['show-ref']) if self.menu_timeout_running<>None: self.canvas.after_cancel(self.menu_timeout_running) self.menu_timeout_running=None self.end_callback(self.show_id,reason,message) self=None return # ********************* # Displaying things # ********************* def display_background(self): pil_menu_img=PIL.Image.open(self.menu_img_file) self.menu_background = PIL.ImageTk.PhotoImage(pil_menu_img) self.drawn = self.canvas.create_image(int(self.canvas['width'])/2, int(self.canvas['height'])/2, image=self.menu_background, anchor=CENTER, tag='pp-content') def display_new_menu(self): # calculate menu geometry error,reason=self.calculate_geometry() if error<>'normal': self.mon.err(self,"Menu geometry error: "+ reason) self.end('error',"Menu geometry error") else: # display the menu entries self.display_menu_entries() def display_menu_entries(self): # init the loop column_index=0 row_index=0 self.menu_length=1 # id store is a list of elements each being a list of the three ids of the elements of the entry self.menu_entry_id=[] # offsets for the above self.icon_id_index=0 # rectangle around the icon self.image_id_index=1 # icon image - needed for tkinter self.text_id_index=2 # the text - need whn no icon is displayed #select the startof the medialist self.medialist.start() #loop through menu entries while True: #display the entry #calculate top left corner of entry self.calculate_entry_position(column_index,row_index) # display the button strip self.display_entry_strip() #display the selected entry highlight icon_id=self.display_icon_rectangle() #display the image in the icon image_id=self.display_icon_image() if self.show_params['menu-text-mode']<>'none': text_id=self.display_icon_text() else: text_id=None #append id's to the list self.menu_entry_id.append([icon_id,image_id,text_id]) self.canvas.update_idletasks( ) #and loop if self.medialist.at_end(): break self.menu_length+=1 self.medialist.next('ordered') if self.direction=='horizontal': column_index+=1 if column_index>=self.menu_columns: column_index=0 row_index+=1 else: row_index+=1 if row_index>=self.menu_rows: row_index=0 column_index+=1 # finally select and highlight the first entry self.medialist.start() self.menu_index=0 self.highlight_menu_entry(self.menu_index,True) def print_geometry(self,total_width,total_height): print 'menu width: ', self.menu_width print 'columns', self.menu_columns print 'icon width: ', self.icon_width print 'horizontal padding: ', self.menu_horizontal_padding print 'text width: ', self.text_width print 'entry width: ', self.entry_width print 'total width: ', total_width print 'x separation: ', self.x_separation print '' print 'menu height', self.menu_height print 'rows: ', self.menu_rows print 'icon height', self.icon_height print 'vertical padding: ', self.menu_vertical_padding print 'text height', self.text_height print 'entry height', self.entry_height print 'total height', total_height print 'y separation', self.y_separation # ------------------------------------------------------------------ #calculate menu entry size and separation between menu entries # ------------------------------------------------------------------ def calculate_geometry(self): self.display_strip=self.show_params['menu-strip'] self.screen_width=int(self.canvas['width']) self.screen_height=int(self.canvas['height']) if self.display_strip=='yes': self.strip_padding=int(self.show_params['menu-strip-padding']) else: self.strip_padding=0 # parse the menu window error,reason,self.menu_x_left,self.menu_y_top,self.menu_x_right,self.menu_y_bottom=self.parse_menu_window(self.show_params['menu-window']) if error<>'normal': return 'error',"Menu Window error: "+ reason if self.show_params['menu-icon-mode']=='none' and self.show_params['menu-text-mode']=='none': return 'error','Icon and Text are both None' if self.show_params['menu-icon-mode']=='none' and self.show_params['menu-text-mode']=='overlay': return 'error','cannot overlay none icon' self.direction=self.show_params['menu-direction'] self.menu_width=self.menu_x_right - self.menu_x_left self.menu_height=self.menu_y_bottom - self.menu_y_top self.list_length=self.medialist.display_length() # get or calculate rows and columns if self.direction=='horizontal': if self.show_params['menu-columns']=='': return 'error','blank columns for horizontal direction' self.menu_columns=int(self.show_params['menu-columns']) self.menu_rows=self.list_length//self.menu_columns if self.list_length % self.menu_columns<>0: self.menu_rows+=1 else: if self.show_params['menu-rows']=='': return 'error','blank rows for vertical direction' self.menu_rows=int(self.show_params['menu-rows']) self.menu_columns=self.list_length//self.menu_rows if self.list_length % self.menu_rows<>0: self.menu_columns+=1 self.x_separation=int(self.show_params['menu-horizontal-separation']) self.y_separation=int(self.show_params['menu-vertical-separation']) # get size of padding depending on exitence of icon and text if self.show_params['menu-icon-mode'] in ('thumbnail','bullet') and self.show_params['menu-text-mode'] == 'right': self.menu_horizontal_padding=int(self.show_params['menu-horizontal-padding']) else: self.menu_horizontal_padding=0 if self.show_params['menu-icon-mode'] in ('thumbnail','bullet') and self.show_params['menu-text-mode'] == 'below': self.menu_vertical_padding=int(self.show_params['menu-vertical-padding']) else: self.menu_vertical_padding=0 #calculate size of icon depending on use if self.show_params['menu-icon-mode'] in ('thumbnail','bullet'): self.icon_width=int(self.show_params['menu-icon-width']) self.icon_height=int(self.show_params['menu-icon-height']) else: self.icon_width=0 self.icon_height=0 #calculate size of text box depending on mode if self.show_params['menu-text-mode']<>'none': self.text_width=int(self.show_params['menu-text-width']) self.text_height=int(self.show_params['menu-text-height']) else: self.text_width=0 self.text_height=0 # calculate size of entry box by combining text and icon sizes if self.show_params['menu-text-mode'] == 'right': self.entry_width=self.icon_width+self.menu_horizontal_padding+self.text_width self.entry_height=max(self.text_height,self.icon_height) elif self.show_params['menu-text-mode']=='below': self.entry_width=max(self.text_width,self.icon_width) self.entry_height=self.icon_height + self.menu_vertical_padding + self.text_height else: # no text or overlaid text if self.show_params['menu-icon-mode'] in ('thumbnail','bullet'): # icon only self.entry_width=self.icon_width self.entry_height=self.icon_height else: #text only self.entry_width=self.text_width self.entry_height=self.text_height if self.entry_width<=self.menu_horizontal_padding: return 'error','entry width is zero' if self.entry_height<=self.menu_vertical_padding: return 'error','entry height is zero' # calculate totals for debugging puropses total_width=self.menu_columns * self.entry_width +(self.menu_columns-1)*self.x_separation total_height=self.menu_rows * self.entry_height + (self.menu_rows-1)*self.y_separation # self.print_geometry(total_width,total_height) # display guidelines and debgging text if there is a problem if total_width>self.menu_width and self.display_guidelines<>'never': self.display_guidelines='always' self.mon.log(self,'\nMENU IS WIDER THAN THE WINDOW') self.print_geometry(total_width,total_height) if total_height>self.menu_height and self.display_guidelines<>'never': self.display_guidelines='always' self.mon.log(self,'\nMENU IS TALLER THAN THE WINDOW') self.print_geometry(total_width,total_height) # display calculated total rectangle guidelines for debugging if self.display_guidelines=='always': points=[self.menu_x_left,self.menu_y_top, self.menu_x_left+total_width,self.menu_y_top+total_height] # and display the icon rectangle self.canvas.create_rectangle(points, outline='red', fill='', tag='pp-content') # display menu rectangle guidelines for debugging if self.display_guidelines=='always': points=[self.menu_x_left,self.menu_y_top, self.menu_x_right,self.menu_y_bottom] self.canvas.create_rectangle(points, outline='blue', fill='', tag='pp-content') return 'normal','' def calculate_entry_position(self,column_index,row_index): self.entry_x=self.menu_x_left+ column_index*(self.x_separation+self.entry_width) self.entry_y=self.menu_y_top+ row_index*(self.y_separation+self.entry_height) def display_entry_strip(self): if self.display_strip=='yes': if self.direction=='vertical': #display the strip strip_points=[self.entry_x - self.strip_padding -1 , self.entry_y - self.strip_padding - 1, self.entry_x+ self.entry_width + self.strip_padding - 1, self.entry_y+self.entry_height+ self.strip_padding - 1] self.canvas.create_rectangle(strip_points, outline='', fill='gray', stipple='gray12', tag='pp-content') top_l_points=[self.entry_x - self.strip_padding, self.entry_y - self.strip_padding, self.entry_x + self.entry_width + self.strip_padding , self.entry_y - self.strip_padding] self.canvas.create_line(top_l_points, fill='light gray', tag='pp-content') bottom_l_points=[self.entry_x - self.strip_padding, self.entry_y + self.entry_height + self.strip_padding, self.entry_x+ self.entry_width + self.strip_padding , self.entry_y+ self.entry_height + self.strip_padding] self.canvas.create_line(bottom_l_points, fill='dark gray', tag='pp-content') left_l_points=[self.entry_x - self.strip_padding, self.entry_y - self.strip_padding, self.entry_x - self.strip_padding, self.entry_y + self.entry_height + self.strip_padding] self.canvas.create_line(left_l_points, fill='gray', tag='pp-content') else: #display the strip vertically strip_points=[self.entry_x - self.strip_padding +1 , self.entry_y - self.strip_padding +1, self.entry_x+self.entry_width + self.strip_padding -1, self.entry_y + self.entry_height+ self.strip_padding -1] self.canvas.create_rectangle(strip_points, outline='', fill='gray', stipple='gray12', tag='pp-content') top_l_points=[self.entry_x - self.strip_padding, self.entry_y - self.strip_padding, self.entry_x + self.entry_width + self.strip_padding, self.entry_y - self.strip_padding] self.canvas.create_line(top_l_points, fill='light gray', tag='pp-content') left_l_points=[self.entry_x - self.strip_padding, self.entry_y - self.strip_padding, self.entry_x - self.strip_padding, self.entry_y + self.entry_height+ self.strip_padding] self.canvas.create_line(left_l_points, fill='gray', tag='pp-content') right_l_points=[self.entry_x +self.entry_width + self.strip_padding, self.entry_y - self.strip_padding, self.entry_x +self.entry_width + self.strip_padding, self.entry_y + self.entry_height+ self.strip_padding] self.canvas.create_line(right_l_points, fill='dark gray', tag='pp-content') # display the rectangle that goes arond the icon when the entry is selected def display_icon_rectangle(self): if self.show_params['menu-icon-mode'] in ('thumbnail','bullet'): #calculate icon parameters if self.icon_width<self.text_width and self.show_params['menu-text-mode']=='below': self.icon_x_left=self.entry_x+abs(self.icon_width-self.text_width)/2 else: self.icon_x_left=self.entry_x self.icon_x_right=self.icon_x_left+self.icon_width if self.icon_height<self.text_height and self.show_params['menu-text-mode']=='right': self.icon_y_top=self.entry_y+abs(self.icon_height-self.text_height)/2 else: self.icon_y_top=self.entry_y self.icon_y_bottom=self.icon_y_top+self.icon_height req_horiz_sep=self.menu_horizontal_padding req_vert_sep=self.menu_vertical_padding points=[self.icon_x_left,self.icon_y_top,self.icon_x_right,self.icon_y_top,self.icon_x_right,self.icon_y_bottom,self.icon_x_left,self.icon_y_bottom] # display guidelines make it white when not selctedfor debugging if self.display_guidelines=='always': outline='white' else: outline='' # and display the icon rectangle icon_id=self.canvas.create_polygon(points, outline=outline, fill='', tag='pp-content') else: # not using icon so set starting point for text to zero icon size self.icon_x_right=self.entry_x self.icon_y_bottom=self.entry_y req_horiz_sep=0 req_vert_sep=0 icon_id=None return icon_id #display the image in a menu entry def display_icon_image(self): image_id=None if self.show_params['menu-icon-mode'] == 'thumbnail': # try for the thumbnail if self.medialist.selected_track()['thumbnail']<>'' and os.path.exists(self.complete_path(self.medialist.selected_track()['thumbnail'])): self.pil_image=PIL.Image.open(self.complete_path(self.medialist.selected_track()['thumbnail'])) else: #cannot find thumbnail get the image if its an image track if self.medialist.selected_track()['type'] =='image': self.track=self.complete_path(self.medialist.selected_track()['location']) else: self.track='' if self.medialist.selected_track()['type']=='image' and os.path.exists(self.track)==True: self.pil_image=PIL.Image.open(self.track) else: #use a standard thumbnail type=self.medialist.selected_track()['type'] standard=self.pp_dir+os.sep+'pp_home'+os.sep+'pp_resources'+os.sep+type+'.png' if os.path.exists(standard)==True: self.pil_image=PIL.Image.open(standard) self.mon.log(self,'WARNING: default thumbnail used for '+self.medialist.selected_track()['title']) else: self.pil_image=None # display the image if self.pil_image<>None: self.pil_image=self.pil_image.resize((self.icon_width-2,self.icon_height-2)) image_id=PIL.ImageTk.PhotoImage(self.pil_image) self.canvas.create_image(self.icon_x_left+1, self.icon_y_top+1, image=image_id, anchor=NW, tag='pp-content') else: image_id=None elif self.show_params['menu-icon-mode'] =='bullet': bullet=self.complete_path(self.show_params['menu-bullet']) if os.path.exists(bullet)==False: self.pil_image=None else: self.pil_image=PIL.Image.open(bullet) if self.pil_image<>None: self.pil_image=self.pil_image.resize((self.icon_width-2,self.icon_height-2)) image_id=PIL.ImageTk.PhotoImage(self.pil_image) self.canvas.create_image(self.icon_x_left+1, self.icon_y_top+1, image=image_id, anchor=NW, tag='pp-content') else: image_id=None return image_id #display the text of a menu entry def display_icon_text(self): text_mode=self.show_params['menu-text-mode'] if self.show_params['menu-icon-mode'] in ('thumbnail','bullet'): if text_mode=='right': if self.icon_height>self.text_height: text_y_top=self.entry_y+abs(self.icon_height-self.text_height)/2 else: text_y_top=self.entry_y text_y_bottom=text_y_top+self.text_height text_x_left=self.icon_x_right+self.menu_horizontal_padding text_x_right=text_x_left+self.text_width text_x=text_x_left text_y=text_y_top+(self.text_height/2) elif text_mode=='below': text_y_top=self.icon_y_bottom+self.menu_vertical_padding text_y_bottom=text_y_top+self.text_height if self.icon_width>self.text_width: text_x_left=self.entry_x+abs(self.icon_width-self.text_width)/2 else: text_x_left=self.entry_x text_x_right=text_x_left+self.text_width text_x=text_x_left+(self.text_width/2) text_y=text_y_top else: # icon with text_mode=overlay or none text_x_left=self.icon_x_left text_x_right= self.icon_x_right text_y_top=self.icon_y_top text_y_bottom=self.icon_y_bottom text_x=(text_x_left+text_x_right)/2 text_y=(text_y_top+text_y_bottom)/2 else: #no icon text only text_y_top=self.entry_y text_y_bottom=text_y_top+self.text_height text_x_left=self.entry_x text_x_right=text_x_left+self.text_width text_x=self.entry_x text_y=self.entry_y+self.text_height/2 #display the guidelines for debugging if self.display_guidelines=='always': points=[text_x_left,text_y_top,text_x_right,text_y_top,text_x_right,text_y_bottom,text_x_left,text_y_bottom] self.canvas.create_polygon(points,fill= '' , outline='white', tag='pp-content') # display the text if text_mode=='below' and self.show_params['menu-icon-mode'] in ('thumbnail','bullet'): anchor=N justify=CENTER elif text_mode=='overlay' and self.show_params['menu-icon-mode'] in ('thumbnail','bullet'): anchor=CENTER justify=CENTER else: anchor=W justify=LEFT text_id=self.canvas.create_text(text_x,text_y, text=self.medialist.selected_track()['title'], anchor=anchor, fill=self.show_params['entry-colour'], font=self.show_params['entry-font'], width=self.text_width, justify=justify, tag='pp-content') return text_id def highlight_menu_entry(self,index,state): if self.show_params['menu-icon-mode']<>'none': if state==True: self.canvas.itemconfig(self.menu_entry_id[index][self.icon_id_index], outline=self.show_params['entry-select-colour'], width=4, ) else: self.canvas.itemconfig(self.menu_entry_id[index][self.icon_id_index], outline='', width=1 ) else: if state==True: self.canvas.itemconfig(self.menu_entry_id[index][self.text_id_index], fill=self.show_params['entry-select-colour']) else: self.canvas.itemconfig(self.menu_entry_id[index][self.text_id_index], fill=self.show_params['entry-colour']) def display_eggtimer(self,text): # print "display eggtimer" self.canvas.create_text(int(self.canvas['width'])/2, int(self.canvas['height'])/2, text= text, fill='white', font="Helvetica 20 bold", tag='pp-eggtimer') self.canvas.update_idletasks( ) def delete_eggtimer(self): # print"delete eggtimer" self.canvas.delete('pp-eggtimer') self.canvas.update_idletasks( ) # ********************* # utilities # ********************* def complete_path(self,track_file): # complete path of the filename of the selected entry if track_file<>'' and track_file[0]=="+": track_file=self.pp_home+track_file[1:] return track_file def parse_menu_window(self,line): if line<>'': fields = line.split() if len(fields) not in (1, 2,4): return 'error','wrong number of fields',0,0,0,0 if len(fields)==1: if fields[0]=='fullscreen': return 'normal','',0,0,self.screen_width - 1, self.screen_height - 1 else: return 'error','single field is not fullscreen',0,0,0,0 if len(fields)==2: if fields[0].isdigit() and fields[1].isdigit(): return 'normal','',int(fields[0]),int(fields[1]),self.screen_width, self.screen_height else: return 'error','field is not a digit',0,0,0,0 if len(fields)==4: if fields[0].isdigit() and fields[1].isdigit() and fields[2].isdigit() and fields[3].isdigit(): return 'normal','',int(fields[0]),int(fields[1]),int(fields[2]),int(fields[3]) else: return 'error','field is not a digit',0,0,0,0 else: return 'error','line is blank',0,0,0,0 def resource(self,section,item): value=self.rr.get(section,item) if value==False: self.mon.err(self, "resource: "+section +': '+ item + " not found" ) # timers may be running so need terminate self.terminate("error") else: return value
class MenuShow: """ Displays a menu with optional hint below it. User can traverse the menu and select a track using key or button presses. Interface: * play - displays the menu and selects the first entry * key_pressed, button_pressed - receives user events passes them to a Player if a track is playing, otherwise actions them with _next, _previous, _play_selected_track, _end Optional display of eggtimer by means of Players ready_callback Supports imageplayer, videoplayer,messagplayer,audioplayer,menushow,mediashow Destroys itself on exit """ # ********************* # external interface # ******************** def __init__(self, show_params, canvas, showlist, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the name of the configuration dictionary section for the menu showlist - the showlist pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory""" self.mon=Monitor() self.mon.on() #instantiate arguments self.show_params=show_params self.showlist=showlist self.canvas=canvas self.pp_home=pp_home self.pp_profile=pp_profile # open resources self.rr=ResourceReader() # init variables self.drawn = None self.player=None self.shower=None self.menu_timeout_running=None self.error=False def play(self,show_id,end_callback,ready_callback=None,top=False,command='nil'): """ displays the menu end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ #instantiate arguments self.show_id=show_id self.end_callback=end_callback self.ready_callback=ready_callback self.top=top self.command=command # check data files are available. self.menu_file = self.pp_profile + "/" + self.show_params['medialist'] if not os.path.exists(self.menu_file): self.mon.err(self,"Medialist file not found: "+ self.menu_file) self._end('error',"Medialist file not found") #create a medialist for the menu and read it. self.medialist=MediaList() if self.medialist.open_list(self.menu_file,self.showlist.sissue()) == False: self.mon.err(self,"Version of medialist different to Pi Presents") self._end('error',"Version of medialist different to Pi Presents") if self.show_params['has-background']=="yes": background_index=self.medialist.index_of_track ('pp-menu-background') if background_index>=0: self.menu_img_file = self.complete_path(self.medialist.track(background_index)) if not os.path.exists(self.menu_img_file): self.mon.err(self,"Menu background file not found: "+ self.menu_img_file) self._end('error',"Menu background file not found") else: self.mon.err(self,"Menu background not found in medialist") self._end('error',"Menu background not found") self.egg_timer=None #start timeout alarm if required if int(self.show_params['timeout'])<>0: self.menu_timeout_running=self.canvas.after(int(self.show_params['timeout'])*1000,self._timeout_menu) if self.ready_callback<>None: self.ready_callback() self.canvas.delete(ALL) # display background image if self.show_params['has-background']=="yes": self._display_background() #display the list of video titles self._display_video_titles() # display instructions (hint) self.canvas.create_text(int(self.canvas['width'])/2, int(self.canvas['height']) - int(self.show_params['hint-y']), text=self.show_params['hint-text'], fill=self.show_params['hint-colour'], font=self.show_params['hint-font']) self.canvas.update( ) # respond to key presses. def key_pressed(self,key_name): self.mon.log(self,"received key: " + key_name) if self.show_params['disable-controls']=='yes': return if key_name=='': pass elif key_name=='escape': # if next lower show eor player is running pass down to stop bottom level # ELSE stop this show if not at top if self.shower<>None: self.shower.key_pressed(key_name) elif self.player<>None: self.player.key_pressed(key_name) else: # not at top so stop the show if self.top == False: self._end('normal',"exit from stop command") else: pass elif key_name in ('up','down'): # if child or sub-show running and is a show pass down # if child not running - move if self.shower<>None: self.shower.key_pressed(key_name) else: if self.player==None: if key_name=='up': self._previous() else: self._next() elif key_name=='return': # if child running and is show - pass down # if no track already running - play if self.shower<>None: self.shower.key_pressed(key_name) else: if self.player==None: self._play_selected_track(self.medialist.selected_track()) elif key_name in ('p',' '): # pass down if show or track running. if self.shower<>None: self.shower.key_pressed(key_name) elif self.player<>None: self.player.key_pressed(key_name) def button_pressed(self,button,edge): if button=='play': self.key_pressed("return") elif button =='up': self.key_pressed("up") elif button=='down': self.key_pressed("down") elif button=='stop': self.key_pressed("escape") elif button=='pause': self.key_pressed('p') # kill or error def terminate(self,reason): if self.shower<>None: self.mon.log(self,"sent terminate to shower") self.shower.terminate(reason) elif self.player<>None: self.mon.log(self,"sent terminate to player") self.player.terminate(reason) else: self._end(reason,'terminated without terminating shower or player') # ********************* # INTERNAL FUNCTIONS # ******************** # ********************* # language resources # ********************* def resource(self,section,item): value=self.rr.get(section,item) if value==False: self.mon.err(self, "resource: "+section +': '+ item + " not found" ) # timers may be running so need terminate self.terminate("error") else: return value # ********************* # Sequencing # ********************* def _timeout_menu(self): self._end('normal','menu timeout') return # finish the player for killing, error or normally # this may be called directly sub/child shows or players are not running # if they might be running then need to call terminate. def _end(self,reason,message): # self.canvas.delete(ALL) # self.canvas.update_idletasks( ) self.mon.log(self,"Ending menushow: "+ self.show_params['show-ref']) if self.menu_timeout_running<>None: self.canvas.after_cancel(self.menu_timeout_running) self.menu_timeout_running=None self.end_callback(self.show_id,reason,message) self=None return def _next(self): self._highlight_menu_entry(self.menu_index,False) self.medialist.next('ordered') if self.menu_index==self.menu_length-1: self.menu_index=0 else: self.menu_index+=1 self._highlight_menu_entry(self.menu_index,True) def _previous(self): self._highlight_menu_entry(self.menu_index,False) if self.menu_index==0: self.menu_index=self.menu_length-1 else: self.menu_index-=1 self.medialist.previous('ordered') self._highlight_menu_entry(self.menu_index,True) # ********************* # Dispatching to Players # ********************* def complete_path(self,selected_track): # complete path of the filename of the selected entry track_file = selected_track['location'] if track_file<>'' and track_file[0]=="+": track_file=self.pp_home+track_file[1:] self.mon.log(self,"Track to play is: "+ track_file) return track_file def _play_selected_track(self,selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ #remove menu and show working..... if self.menu_timeout_running<>None: self.canvas.after_cancel(self.menu_timeout_running) self.menu_timeout_running=None self.canvas.delete(ALL) self._display_eggtimer(self.resource('menushow','m01')) # dispatch track by type self.player=None self.shower=None track_type = selected_track['type'] self.mon.log(self,"Track type is: "+ track_type) if track_type=="video": # create a videoplayer track_file=self.complete_path(selected_track) self.player=VideoPlayer(self.show_id,self.canvas,self.pp_home,self.show_params,selected_track) self.player.play(track_file, self._end_player, self._delete_eggtimer, enable_menu=False) elif track_type=="audio": # create a audioplayer track_file=self.complete_path(selected_track) self.player=AudioPlayer(self.show_id,self.canvas,self.pp_home,self.show_params,selected_track) self.player.play(track_file, self._end_player, self._delete_eggtimer, enable_menu=False) elif track_type=="image": # images played from menus don't have children track_file=self.complete_path(selected_track) self.player=ImagePlayer(self.show_id,self.canvas,self.pp_home,self.show_params,selected_track) self.player.play(track_file, self._end_player, self._delete_eggtimer, enable_menu=False, ) elif track_type=="message": # bit odd because MessagePlayer is used internally to display text. text=selected_track['text'] self.player=MessagePlayer(self.show_id,self.canvas,self.pp_home,self.show_params,selected_track) self.player.play(text, self._end_player, self._delete_eggtimer, enable_menu=False ) elif track_type=="show": # get the show from the showlist index = self.showlist.index_of_show(selected_track['sub-show']) if index >=0: self.showlist.select(index) selected_show=self.showlist.selected_show() else: self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show']) self._end("Unknown show") if selected_show['type']=="mediashow": self.shower= MediaShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self._end_shower,top=False,command='nil') elif selected_show['type']=="liveshow": self.shower= LiveShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,top=False,command='nil') elif selected_show['type']=="menu": self.shower= MenuShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self._end_shower,top=False,command='nil') else: self.mon.err(self,"Unknown Show Type: "+ selected_show['type']) self._end("Unknown show type") else: self.mon.err(self,"Unknown Track Type: "+ track_type) self._end("Unknown track type") # callback from when player ends def _end_player(self,reason,message): self.mon.log(self,"Returned from player with message: "+ message) self.player=None if reason in("killed","error"): self._end(reason,message) self._display_eggtimer(self.resource('menushow','m02')) self._what_next(message) # callback from when shower ends def _end_shower(self,show_id,reason,message): self.mon.log(self,"Returned from shower with message: "+ message) self.shower=None if message in ("killed","error"): self._end(reason,message) self._display_eggtimer(self.resource('menushow','m03')) self._what_next(message) # at the end of a track just re-display the menu with the original callback from the menu def _what_next(self,message): self.mon.log(self,"Re-displaying menu") self.play(self.show_id,self.end_callback,top=self.top) # ********************* # Displaying things # ********************* def _display_background(self): pil_menu_img=PIL.Image.open(self.menu_img_file) # adjust brightness and rotate (experimental) # enh=PIL.ImageEnhance.Brightness(pil_menu_img) # pil_menu_img=enh.enhance(0.1) # pil_menu_img=pil_menu_img.rotate(45) self.menu_background = PIL.ImageTk.PhotoImage(pil_menu_img) self.drawn = self.canvas.create_image(int(self.canvas['width'])/2, int(self.canvas['height'])/2, image=self.menu_background, anchor=CENTER) def _display_video_titles(self): self.menu_length=1 self.menu_entry_id=[] x=int(self.show_params['menu-x']) y=int(self.show_params['menu-y']) self.medialist.start() while True: id=self.canvas.create_text(x,y,anchor=NW, text="* "+self.medialist.selected_track()['title'], fill=self.show_params['entry-colour'], font=self.show_params['entry-font']) self.menu_entry_id.append(id) y=y + int(self.show_params['menu-spacing']) if self.medialist.at_end(): break self.menu_length+=1 self.medialist.next('ordered') # select and highlight the first entry self.medialist.start() self.menu_index=0 self._highlight_menu_entry(self.menu_index,True) # self.medialist.print_list() def _highlight_menu_entry(self,index,state): if state==True: self.canvas.itemconfig(self.menu_entry_id[index],fill=self.show_params['entry-select-colour']) else: self.canvas.itemconfig(self.menu_entry_id[index],fill=self.show_params['entry-colour']) def _display_eggtimer(self,text): self.egg_timer=self.canvas.create_text(int(self.canvas['width'])/2, int(self.canvas['height'])/2, text= text, fill='white', font="Helvetica 20 bold") self.canvas.update_idletasks( ) def _delete_eggtimer(self): if self.egg_timer!=None: self.canvas.delete(self.egg_timer)
class MenuShow: """ Displays a menu with optional hint below it. User can traverse the menu and select a track using key or button presses. Interface: * play - displays the menu and selects the first entry * key_pressed, button_pressed - receives user events passes them to a Player if a track is playing, otherwise actions them with _next, _previous, _play_selected_track, _end Optional display of eggtimer by means of Players ready_callback Supports imageplayer, videoplayer,messagplayer,menushow,mediashow Destroys itself on exit """ # ********************* # external interface # ******************** def __init__(self, show, canvas, showlist, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the name of the configuration dictionary section for the menu cf - the configuration object pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory""" self.mon=Monitor() self.mon.on() #instantiate arguments self.show=show self.showlist=showlist self.canvas=canvas self.pp_home=pp_home self.pp_profile=pp_profile # open resources self.rr=ResourceReader() # init variables self.drawn = None self.player=None self.shower=None self.menu_timeout_running=None self.error=False def play(self,end_callback,ready_callback=None,top=False,command='nil'): """ displays the menu end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ #instantiate arguments self.end_callback=end_callback self.ready_callback=ready_callback self.top=top self.command=command # check data files are available. self.menu_file = self.pp_profile + "/" + self.show['medialist'] if not os.path.exists(self.menu_file): self.mon.err(self,"Medialist file not found: "+ self.menu_file) self._end('error',"Medialist file not found") #create a medialist for the menu and read it. self.medialist=MediaList() if self.medialist.open_list(self.menu_file,self.showlist.sissue()) == False: self.mon.err(self,"Version of medialist different to Pi Presents") self._end('error',"Version of medialist different to Pi Presents") if self.show['has-background']=="yes": background_index=self.medialist.index_of_track ('pp-menu-background') if background_index>=0: self.menu_img_file = self.complete_path(self.medialist.track(background_index)) if not os.path.exists(self.menu_img_file): self.mon.err(self,"Menu background file not found: "+ self.menu_img_file) self._end('error',"Menu background file not found") else: self.mon.err(self,"Menu background not found in medialist") self._end('error',"Menu background not found") #start timeout alarm if required if int(self.show['timeout'])<>0: self.menu_timeout_running=self.canvas.after(int(self.show['timeout'])*1000,self._timeout_menu) if self.ready_callback<>None: self.ready_callback() self.canvas.delete(ALL) # display background image if self.show['has-background']=="yes": self._display_background() #display the list of video titles self._display_video_titles() # display instructions (hint) self.canvas.create_text(int(self.canvas['width'])/2, int(self.canvas['height']) - int(self.show['hint-y']), text=self.show['hint-text'], fill=self.show['hint-colour'], font=self.show['hint-font']) self.canvas.update( ) # respond to key presses. def key_pressed(self,key_name): self.mon.log(self,"received key: " + key_name) if key_name=='': pass elif key_name=='escape': # if next lower show eor player is running pass down to stop bottom level # ELSE stop this show if not at top if self.shower<>None: self.shower.key_pressed(key_name) elif self.player<>None: self.player.key_pressed(key_name) else: # not at top so stop the show if self.top == False: self._end('normal',"exit from stop command") else: pass elif key_name in ('up','down'): # if child or sub-show running and is a show pass down # if child not running - move if self.shower<>None: self.shower.key_pressed(key_name) else: if self.player==None: if key_name=='up': self._previous() else: self._next() elif key_name=='return': # if child running and is show - pass down # if no track already running - play if self.shower<>None: self.shower.key_pressed(key_name) else: if self.player==None: self._play_selected_track(self.medialist.selected_track()) elif key_name in ('p',' '): # pass down if show or track running. if self.shower<>None: self.shower.key_pressed(key_name) elif self.player<>None: self.player.key_pressed(key_name) def button_pressed(self,button,edge): if button=='play': self.key_pressed("return") elif button =='up': self.key_pressed("up") elif button=='down': self.key_pressed("down") elif button=='stop': self.key_pressed("escape") elif button=='pause': self.key_pressed('p') # kill or error def terminate(self,reason): if self.shower<>None: self.mon.log(self,"sent terminate to shower") self.shower.terminate(reason) elif self.player<>None: self.mon.log(self,"sent terminate to player") self.player.terminate(reason) else: self._end(reason,'terminated without terminating shower or player') # ********************* # INTERNAL FUNCTIONS # ******************** # ********************* # language resources # ********************* def resource(self,section,item): value=self.rr.get(section,item) if value==False: self.mon.err(self, "resource: "+section +': '+ item + " not found" ) # timers may be running so need terminate self.terminate("error") else: return value # ********************* # Sequencing # ********************* def _timeout_menu(self): self._end('normal','menu timeout') return # finish the player for killing, error or normally # this may be called directly sub/child shows or players are not running # if they might be running then need to call terminate. def _end(self,reason,message): self.canvas.delete(ALL) self.canvas.update_idletasks( ) self.mon.log(self,"Ending menushow: "+ self.show['show-ref']) if self.menu_timeout_running<>None: self.canvas.after_cancel(self.menu_timeout_running) self.menu_timeout_running=None self.end_callback(reason,message) self=None return def _next(self): self._highlight_menu_entry(self.menu_index,False) self.medialist.next() if self.menu_index==self.menu_length-1: self.menu_index=0 else: self.menu_index+=1 self._highlight_menu_entry(self.menu_index,True) def _previous(self): self._highlight_menu_entry(self.menu_index,False) if self.menu_index==0: self.menu_index=self.menu_length-1 else: self.menu_index-=1 self.medialist.previous() self._highlight_menu_entry(self.menu_index,True) # ********************* # Dispatching to Players # ********************* def complete_path(self,selected_track): # complete path of the filename of the selected entry track_file = selected_track['location'] if track_file[0]=="+": track_file=self.pp_home+track_file[1:] self.mon.log(self,"Track to play is: "+ track_file) return track_file def _play_selected_track(self,selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ #remove menu and show working..... self.canvas.delete(ALL) if self.menu_timeout_running<>None: self.canvas.after_cancel(self.menu_timeout_running) self.menu_timeout_running=None self._display_eggtimer(self.resource('menushow','m01')) # dispatch track by type self.player=None self.shower=None track_type = selected_track['type'] self.mon.log(self,"Track type is: "+ track_type) if track_type=="video": # create a videoplayer track_file=self.complete_path(selected_track) self.player=VideoPlayer(self.canvas,self.show,selected_track) self.player.play(track_file, self._end_player, self._delete_eggtimer, enable_menu=False) elif track_type=="image": # images played from menus don't have children enable_child=False track_file=self.complete_path(selected_track) self.player=ImagePlayer(self.canvas,self.show,selected_track) self.player.play(track_file, self._end_player, self._delete_eggtimer, enable_menu=enable_child, ) elif track_type=="message": # bit odd because MessagePlayer is used internally to display text. text=selected_track['text'] self.player=MessagePlayer(self.canvas,self.show,selected_track) self.player.play(text, self._end_player, self._delete_eggtimer, enable_menu=False ) elif track_type=="show": # get the show from the showlist index = self.showlist.index_of_show(selected_track['sub-show']) if index >=0: self.showlist.select(index) selected_show=self.showlist.selected_show() else: self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show']) self._end("Unknown show") if selected_show['type']=="mediashow": self.shower= MediaShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self._end_shower,top=False,command='nil') elif selected_show['type']=="liveshow": self.shower= LiveShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower,top=False,command='nil') elif selected_show['type']=="menu": self.shower= MenuShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self._end_shower,top=False,command='nil') else: self.mon.err(self,"Unknown Show Type: "+ selected_show['type']) self._end("Unknown show type") else: self.mon.err(self,"Unknown Track Type: "+ track_type) self._end("Unknown track type") # callback from when player ends def _end_player(self,reason,message): self.mon.log(self,"Returned from player with message: "+ message) self.player=None if reason in("killed","error"): self._end(reason,message) self._display_eggtimer(self.resource('menushow','m02')) self._what_next(message) # callback from when shower ends def _end_shower(self,reason,message): self.mon.log(self,"Returned from shower with message: "+ message) self.shower=None if message in ("killed","error"): self._end(reason,message) self._display_eggtimer(self.resource('menushow','m03')) self._what_next(message) # at the end of a track just re-display the menu with the original callback from the menu def _what_next(self,message): self.mon.log(self,"Re-displaying menu") self.play(self.end_callback,top=self.top) # ********************* # Displaying things # ********************* def _display_background(self): pil_menu_img=PIL.Image.open(self.menu_img_file) # adjust brightness and rotate (experimental) # enh=PIL.ImageEnhance.Brightness(pil_menu_img) # pil_menu_img=enh.enhance(0.1) # pil_menu_img=pil_menu_img.rotate(45) self.menu_background = PIL.ImageTk.PhotoImage(pil_menu_img) self.drawn = self.canvas.create_image(int(self.canvas['width'])/2, int(self.canvas['height'])/2, image=self.menu_background, anchor=CENTER) def _display_video_titles(self): self.menu_length=1 self.menu_entry_id=[] x=int(self.show['menu-x']) y=int(self.show['menu-y']) self.medialist.start() while True: id=self.canvas.create_text(x,y,anchor=NW, text="* "+self.medialist.selected_track()['title'], fill=self.show['entry-colour'], font=self.show['entry-font']) self.menu_entry_id.append(id) y=y + int(self.show['menu-spacing']) if self.medialist.at_end(): break self.menu_length+=1 self.medialist.next() # select and highlight the first entry self.medialist.start() self.menu_index=0 self._highlight_menu_entry(self.menu_index,True) # self.medialist.print_list() def _highlight_menu_entry(self,index,state): if state==True: self.canvas.itemconfig(self.menu_entry_id[index],fill=self.show['entry-select-colour']) else: self.canvas.itemconfig(self.menu_entry_id[index],fill=self.show['entry-colour']) def _display_eggtimer(self,text): self.canvas.create_text(int(self.canvas['width'])/2, int(self.canvas['height'])/2, text= text, fill='white', font="Helvetica 20 bold") self.canvas.update_idletasks( ) def _delete_eggtimer(self): self.canvas.delete(ALL)
class RadioMediaShow(Show): def __init__(self, show_id, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile, command_callback): # init the common bits Show.base__init__(self, show_id, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile, command_callback) # instatiatate the screen driver - used only to access enable and hide click areas self.sr = ScreenDriver() # create an instance of PathManager - only used to parse the links. self.path = PathManager() self.allowed_links = ('play', 'pause', 'exit', 'return', 'null', 'no-command', 'stop') # init variables self.links = [] self.track_timeout_timer = None self.show_timeout_timer = None self.next_track_signal = False self.current_track_ref = '' self.req_next = '' self.controlsmanager = ControlsManager() # Init variables special to this show self.poll_for_interval_timer = None self.interval_timer_signal = False self.waiting_for_interval = False self.interval_timer = None self.duration_timer = None self.end_trigger_signal = False self.next_track_signal = False self.previous_track_signal = False self.play_child_signal = False self.error_signal = False self.show_timeout_signal = False self.req_next = 'nil' self.state = 'closed' self.count = 0 self.interval = 0 self.duration = 0 self.controls_list = [] self.enable_hint = True # def play(self,end_callback,show_ready_callback, direction_command,level,controls_list): # # # use the appropriate medialist # self.medialist=MediaList(self.show_params['sequence']) # # GapShow.play(self,end_callback,show_ready_callback, direction_command,level,controls_list) def play(self, end_callback, show_ready_callback, parent_kickback_signal, level, controls_list): # use the appropriate medialist self.medialist = MediaList(self.show_params['sequence']) self.mon.newline(3) self.mon.trace(self, self.show_params['show-ref']) Show.base_play(self, end_callback, show_ready_callback, parent_kickback_signal, level, controls_list) # unpack show parameters reason, message, self.show_timeout = Show.calculate_duration( self, self.show_params['show-timeout']) if reason == 'error': self.mon.err( self, 'ShowTimeout has bad time: ' + self.show_params['show-timeout']) self.end( 'error', 'ShowTimeout has bad time: ' + self.show_params['show-timeout']) self.track_count_limit = int(self.show_params['track-count-limit']) reason, message, self.interval = Show.calculate_duration( self, self.show_params['interval']) if reason == 'error': self.mon.err( self, 'Interval has bad time: ' + self.show_params['interval']) self.end('error', 'Interval has bad time: ' + self.show_params['interval']) # delete eggtimer started by the parent if self.previous_shower is not None: self.previous_shower.delete_eggtimer() self.start_show() # respond to inputs def handle_input_event(self, symbol): Show.base_handle_input_event(self, symbol) #self.handle_input_event_this_show(symbol) def handle_input_event_this_show(self, symbol): # for radiobuttonshow the symbolic names are links to play tracks, also a limited number of in-track operations # find the first entry in links that matches the symbol and execute its operation #print 'radiomediashow ', symbol found, link_op, link_arg = self.path.find_link(symbol, self.links) #print 'input event', symbol, link_op if found is True: if link_op == 'play': #print 'playing ' + link_arg self.do_play(link_arg) elif link_op == 'exit': #exit the show self.exit() elif link_op == 'stop': self.stop_timers() if self.current_player is not None: if self.current_track_ref == self.first_track_ref and self.level != 0: # if quiescent then set signal to stop the show when track has stopped self.user_stop_signal = True self.current_player.input_pressed('stop') elif link_op == 'return': # return to the first track if self.current_track_ref != self.first_track_ref: self.do_play(self.first_track_ref) # in-track operations elif link_op == 'pause': if self.current_player is not None: self.current_player.input_pressed(link_op) elif link_op in ('no-command', 'null'): return elif link_op[0:4] == 'omx-' or link_op[0:6] == 'mplay-' or link_op[ 0:5] == 'uzbl-': if self.current_player is not None: self.current_player.input_pressed(link_op) else: self.mon.err(self, "unknown link command: " + link_op) self.end('error', "unknown link command: " + link_op) def do_play(self, track_ref): #print 'do_play ' + track_ref # if track_ref != self.current_track_ref: # cancel the show timeout when playing another track if self.show_timeout_timer is not None: self.canvas.after_cancel(self.show_timeout_timer) self.show_timeout_timer = None # print '\n NEED NEXT TRACK' #self.next_track_signal=True self.next_track_op = 'play' self.next_track_arg = track_ref self.play_child_signal = True if self.shower is not None: #print 'current_shower not none so stopping',self.mon.id(self.current_shower) self.shower.do_operation('stop') elif self.current_player is not None: #print 'current_player not none so stopping',self.mon.id(self.current_player), ' for' ,track_ref self.current_player.input_pressed('stop') else: return # *************************** # Show sequencing # *************************** def start_show(self): # initial direction from parent show self.kickback_for_next_track = self.parent_kickback_signal # print '\n\ninital KICKBACK from parent', self.kickback_for_next_track # start duration timer if self.show_timeout != 0: # print 'set alarm ', self.show_timeout self.duration_timer = self.canvas.after(self.show_timeout * 1000, self.show_timeout_stop) self.first_list = True # and start the first list of the show self.wait_for_trigger() def wait_for_trigger(self): # wait for trigger sets the state to waiting so that trigger events can do a start_list. self.state = 'waiting' self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Waiting for trigger: " + self.show_params['trigger-start-type']) if self.show_params['trigger-start-type'] == "input": #close the previous track to display admin message Show.base_shuffle(self) Show.base_track_ready_callback(self, False) Show.display_admin_message(self, self.show_params['trigger-wait-text']) elif self.show_params['trigger-start-type'] == "input-persist": if self.first_list == True: #first time through track list so play the track without waiting to get to end. self.first_list = False self.start_list() else: #wait for trigger while displaying previous track pass elif self.show_params['trigger-start-type'] == "start": # don't close the previous track to give seamless repeat of the show self.start_list() else: self.mon.err( self, "Unknown trigger: " + self.show_params['trigger-start-type']) self.end( 'error', "Unknown trigger: " + self.show_params['trigger-start-type']) def start_list(self): # starts the list or any repeat having waited for trigger first. self.state = 'playing' # initialise track counter for the list self.track_count = 0 # start interval timer self.interval_timer_signal = False if self.interval != 0: self.interval_timer = self.canvas.after(self.interval * 1000, self.end_interval_timer) #get rid of previous track in order to display the empty message if self.medialist.display_length() == 0: Show.base_shuffle(self) Show.base_track_ready_callback(self, False) Show.display_admin_message(self, self.show_params['empty-text']) self.wait_for_not_empty() else: self.not_empty() def wait_for_not_empty(self): if self.medialist.display_length() == 0: # list is empty retry after 5 secs self.canvas.after(5000, self.wait_for_not_empty) else: Show.delete_admin_message(self) self.not_empty() def not_empty(self): #get first or last track depending on direction # print 'use direction for start or end of list', self.kickback_for_next_track if self.kickback_for_next_track is True: self.medialist.finish() else: self.medialist.start() self.start_load_show_loop(self.medialist.selected_track()) # *************************** # Track load/show loop # *************************** # track playing loop starts here def start_load_show_loop(self, selected_track): # uncomment the next line to write stats for every track # Show.write_stats(self,'play a track',self.show_params,selected_track) # shuffle players Show.base_shuffle(self) self.delete_eggtimer() # is child track required if self.show_params['child-track-ref'] != '': self.enable_child = True else: self.enable_child = False # read the show links. Track links will be added by ready_callback # needs to be done in show loop as each track adds different links to the show links if self.show_params['disable-controls'] == 'yes': self.links = [] else: reason, message, self.links = self.path.parse_links( self.show_params['links'], self.allowed_links) if reason == 'error': self.mon.err(self, message + " in show") self.end('error', message + " in show") # load the track or show # params - track,enable_menu enable = self.enable_child & self.enable_hint Show.base_load_track_or_show(self, selected_track, self.what_next_after_load, self.end_shower, enable) # track has loaded so show it. def what_next_after_load(self, status, message): self.mon.log( self, 'Show Id ' + str(self.show_id) + ' load complete with status: ' + status + ' message: ' + message) if self.current_player.play_state == 'load-failed': self.error_signal = True self.what_next_after_showing() else: if self.terminate_signal is True or self.exit_signal is True or self.user_stop_signal is True: self.what_next_after_showing() else: self.mon.trace(self, ' - showing track') self.current_player.show(self.track_ready_callback, self.finished_showing, self.closed_after_showing) def finished_showing(self, reason, message): self.sr.hide_click_areas(self.controls_list) if self.current_player.play_state == 'show-failed': self.error_signal = True else: self.req_next = 'finished-player' # showing has finished with 'pause at end', showing the next track will close it after next has started showing self.mon.trace(self, ' - pause at end ') self.mon.log( self, "pause at end of showing track with reason: " + reason + ' and message: ' + message) self.what_next_after_showing() def closed_after_showing(self, reason, message): self.sr.hide_click_areas(self.controls_list) if self.current_player.play_state == 'show-failed': self.error_signal = True else: self.req_next = 'closed-player' # showing has finished with closing of player but track instance is alive for hiding the x_content self.mon.trace(self, ' - closed') self.mon.log( self, "Closed after showing track with reason: " + reason + ' and message: ' + message) self.what_next_after_showing() # subshow or child show has ended def end_shower(self, show_id, reason, message): self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ': Returned from shower with ' + reason + ' ' + message) self.sr.hide_click_areas(self.controls_list) self.req_next = reason Show.base_end_shower(self) self.what_next_after_showing() def pretty_what_next_after_showing_state(self): state = '\n* terminate signal ' + str(self.terminate_signal) state += '\n* error signal ' + str(self.error_signal) state += '\n* req_next used to indicate subshow reports an error ' + self.req_next state += '\n* exit signal ' + str(self.exit_signal) state += '\n* show timeout signal ' + str(self.show_timeout_signal) state += '\n* user stop signal ' + str(self.user_stop_signal) state += '\n* previous track signal ' + str( self.previous_track_signal) state += '\n* next track signal ' + str(self.next_track_signal) state += '\n* kickback from subshow ' + str( self.subshow_kickback_signal) return state + '\n' def what_next_after_showing(self): self.mon.trace(self, self.pretty_what_next_after_showing_state()) self.track_count += 1 # set false when child rack is to be played self.enable_hint = True # first of all deal with conditions that do not require the next track to be shown # some of the conditions can happen at any time, others only when a track is closed or at pause_at_end # need to terminate if self.terminate_signal is True: self.terminate_signal = False self.stop_timers() # set what to do after closed or unloaded self.ending_reason = 'killed' Show.base_close_or_unload(self) elif self.error_signal == True or self.req_next == 'error': self.error_signal = False self.req_next = '' self.stop_timers() # set what to do after closed or unloaded self.ending_reason = 'error' Show.base_close_or_unload(self) # used for exiting show from other shows, time of day, external etc. elif self.exit_signal is True: self.exit_signal = False self.stop_timers() self.ending_reason = 'exit' Show.base_close_or_unload(self) # show timeout elif self.show_timeout_signal is True: self.show_timeout_signal = False self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) # user wants to stop the show elif self.user_stop_signal is True: self.user_stop_signal = False self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) # interval > 0. If last track has finished we are waiting for interval timer before ending the list # note: if medialist finishes after interval is up then this route is used to start trigger. elif self.waiting_for_interval is True: # interval_timer_signal set by alarm clock started in start_list if self.interval_timer_signal is True: self.interval_timer_signal = False self.waiting_for_interval = False # print 'RECEIVED INTERVAL TIMER SIGNAL' if self.show_params['repeat'] == 'repeat': self.wait_for_trigger() else: self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) else: self.poll_for_interval_timer = self.canvas.after( 1000, self.what_next_after_showing) # has content of list been changed (replaced if it has, used for content of livelist) # causes it to go to wait_for_trigger and start_list #not sure why need clos_and_unload here ??????? elif self.medialist.replace_if_changed() is True: self.ending_reason = 'change-medialist' # print 'CHANGE REQ' Show.base_close_or_unload(self) # otherwise consider operation that might show the next track else: # setup default direction for next track as normally goes forward unless kicked back self.kickback_for_next_track = False # print 'init direction to False at begin of what_next_after showing', self.kickback_for_next_track # end trigger from input, or track count if self.end_trigger_signal is True or (self.track_count_limit > 0 and self.track_count == self.track_count_limit): self.end_trigger_signal = False # repeat so test start trigger if self.show_params['repeat'] == 'repeat': self.stop_timers() # print 'END TRIGGER restart' self.wait_for_trigger() else: # single run so exit show if self.level != 0: self.kickback_for_next_track = False self.subshow_kickback_signal = False self.end('normal', "End of single run - Return from Sub Show") else: # end of single run and at top - exit the show self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) # user wants to play child elif self.play_child_signal is True: self.play_child_signal = False # index = self.medialist.index_of_track(self.child_track_ref) # if index >=0: # # don't use select the track as need to preserve mediashow sequence for returning from child # child_track=self.medialist.track(index) # Show.write_stats(self,'play child',self.show_params,child_track) # self.display_eggtimer() # self.enable_hint=False # self.start_load_show_loop(child_track) # else: # self.mon.err(self,"Child not found in medialist: "+ self.child_track_ref) # self.ending_reason='error' # Show.base_close_or_unload(self) # play user selected track # HOOK ! self.current_track_ref = self.next_track_arg #print 'what next - next track signal is True so load ', self.current_track_ref index = self.medialist.index_of_track(self.current_track_ref) if index >= 0: # don't use select the track as not using selected_track in radiobuttonshow # and load it Show.write_stats(self, 'play', self.show_params, self.medialist.track(index)) self.display_eggtimer() self.enable_hint = False self.start_load_show_loop(self.medialist.track(index)) else: self.mon.err( self, "next track not found in medialist: " + self.current_track_ref) self.end( 'error', "next track not found in medialist: " + self.current_track_ref) Show.base_close_or_unload(self) # skip to next track on user input or after subshow elif self.next_track_signal is True: #print 'skip forward test' ,self.subshow_kickback_signal if self.next_track_signal is True or self.subshow_kickback_signal is False: self.next_track_signal = False self.kickback_for_next_track = False if self.medialist.at_end() is True: # medialist_at_end can give false positive for shuffle if self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'repeat': self.wait_for_trigger() elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'single-run': if self.level != 0: self.kickback_for_next_track = False self.subshow_kickback_signal = False # print 'end subshow skip forward test, self. direction is ' ,self.kickback_for_next_track self.end('normal', "Return from Sub Show") else: # end of single run and at top - exit the show self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) else: # shuffling - just do next track self.kickback_for_next_track = False self.medialist.next(self.show_params['sequence']) self.start_load_show_loop( self.medialist.selected_track()) else: # not at end just do next track self.medialist.next(self.show_params['sequence']) self.start_load_show_loop( self.medialist.selected_track()) # skip to previous track on user input or after subshow elif self.previous_track_signal is True or self.subshow_kickback_signal is True: #print 'skip backward test, subshow kickback is' ,self.subshow_kickback_signal self.subshow_kickback_signal = False self.previous_track_signal = False self.kickback_for_next_track = True # medialist_at_start can give false positive for shuffle if self.medialist.at_start() is True: # print 'AT START' if self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'repeat': self.kickback_for_next_track = True self.wait_for_trigger() elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'single-run': if self.level != 0: self.kickback_for_next_track = True self.subshow_kickback_signal = True # print 'end subshow skip forward test, self. direction is ' ,self.kickback_for_next_track self.end('normal', "Return from Sub Show") else: # end of single run and at top - exit the show self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) else: # shuffling - just do previous track self.kickback_for_next_track = True self.medialist.previous(self.show_params['sequence']) self.start_load_show_loop( self.medialist.selected_track()) else: # not at end just do next track self.medialist.previous(self.show_params['sequence']) self.start_load_show_loop(self.medialist.selected_track()) # AT END OF MEDIALIST elif self.medialist.at_end() is True: #print 'MEDIALIST AT END' # interval>0 and list finished so wait for the interval timer if self.show_params[ 'sequence'] == "ordered" and self.interval > 0 and self.interval_timer_signal == False: self.waiting_for_interval = True # print 'WAITING FOR INTERVAL' Show.base_shuffle(self) Show.base_track_ready_callback(self, False) self.poll_for_interval_timer = self.canvas.after( 200, self.what_next_after_showing) # interval=0 #elif self.show_params['sequence'] == "ordered" and self.show_params['repeat'] == 'repeat' and self.show_params['trigger-end-type']== 'interval' and int(self.show_params['trigger-end-param']) == 0: #self.medialist.next(self.show_params['sequence']) # self.start_load_show_loop(self.medialist.selected_track()) # shuffling so there is no end condition, get out of end test elif self.show_params['sequence'] == "shuffle": self.medialist.next(self.show_params['sequence']) self.start_load_show_loop(self.medialist.selected_track()) # nothing special to do at end of list, just repeat or exit elif self.show_params['sequence'] == "ordered": if self.show_params['repeat'] == 'repeat': self.wait_for_trigger() else: # single run # if not at top return to parent if self.level != 0: self.end('normal', "End of Single Run") else: # at top so close the show self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) else: self.mon.err( self, "Unhandled playing event at end of list: " + self.show_params['sequence'] + ' with ' + self.show_params['repeat'] + " of " + self.show_params['trigger-end-param']) self.end( 'error', "Unhandled playing event at end of list: " + self.show_params['sequence'] + ' with ' + self.show_params['repeat'] + " of " + self.show_params['trigger-end-param']) elif self.medialist.at_end() is False: # nothing special just do the next track self.medialist.next(self.show_params['sequence']) self.start_load_show_loop(self.medialist.selected_track()) else: # unhandled state self.mon.err( self, "Unhandled playing event: " + self.show_params['sequence'] + ' with ' + self.show_params['repeat'] + " of " + self.show_params['trigger-end-param']) self.end( 'error', "Unhandled playing event: " + self.show_params['sequence'] + ' with ' + self.show_params['repeat'] + " of " + self.show_params['trigger-end-param']) # ********************* # Interface with other shows/players to reduce black gaps # ********************* # # called just before a track is shown to remove the previous track from the screen # # and if necessary close it # def track_ready_callback(self,enable_show_background): # self.delete_eggtimer() # # # get control bindings for this show # # needs to be done for each track as track can override the show controls # if self.show_params['disable-controls'] == 'yes': # self.controls_list=[] # else: # reason,message,self.controls_list= self.controlsmanager.get_controls(self.show_params['controls']) # if reason=='error': # self.mon.err(self,message) # self.end('error',"error in controls: " + message) # return # # # print 'controls',reason,self.show_params['controls'],self.controls_list # #merge controls from the track # controls_text=self.current_player.get_links() # reason,message,track_controls=self.controlsmanager.parse_controls(controls_text) # if reason == 'error': # self.mon.err(self,message + " in track: "+ self.current_player.track_params['track-ref']) # self.error_signal=True # self.what_next_after_showing() # self.controlsmanager.merge_controls(self.controls_list,track_controls) # # # enable the click-area that are in the list of controls # self.sr.enable_click_areas(self.controls_list) # Show.base_track_ready_callback(self,enable_show_background) # called just before a track is shown to remove the previous track from the screen # and if necessary close it def track_ready_callback(self, enable_show_background): self.delete_eggtimer() # print 'TRACK READY CALLBACK' # print 'previous is',self.mon.id(self.previous_player), self.next_track_signal #merge links from the track if self.show_params['disable-controls'] == 'yes': track_links = [] else: reason, message, track_links = self.path.parse_links( self.current_player.get_links(), self.allowed_links) if reason == 'error': self.mon.err( self, message + " in track: " + self.current_player.track_params['track-ref']) self.req_next = 'error' self.what_next_after_showing() self.path.merge_links(self.links, track_links) # enable the click-area that are in the list of links self.sr.enable_click_areas(self.links) Show.base_track_ready_callback(self, enable_show_background) # callback from begining of a subshow, provide previous player to called show def subshow_ready_callback(self): return Show.base_subshow_ready_callback(self) # ********************* # End the show # ********************* def end(self, reason, message): Show.base_end(self, reason, message) def stop_timers(self): # clear outstanding time of day events for this show # self.tod.clear_times_list(id(self)) if self.poll_for_interval_timer is not None: self.canvas.after_cancel(self.poll_for_interval_timer) self.poll_for_interval_timer = None if self.interval_timer is not None: self.canvas.after_cancel(self.interval_timer) self.interval_timer = None if self.duration_timer is not None: self.canvas.after_cancel(self.duration_timer) self.duration_timer = None
class MenuShow(Show): def __init__(self, show_id, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile, command_callback): """ show_id - index of the top level show caling this (for debug only) show_params - dictionary section for the menu canvas - the canvas that the menu is to be written on showlist - the showlist pp_dir - Pi Presents directory pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory """ # init the common bits Show.base__init__(self, show_id, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile, command_callback) # instatiatate the screen driver - used only to access enable and hide click areas self.sr=ScreenDriver() self.controlsmanager=ControlsManager() # init variables self.show_timeout_timer=None self.track_timeout_timer=None self.next_track_signal=False self.next_track=None self.menu_index=0 self.menu_showing=True self.req_next='' def play(self,end_callback,show_ready_callback,parent_kickback_signal,level,controls_list): """ displays the menu end_callback - function to be called when the menu exits show_ready_callback - callback when menu is ready to display (not used) level is 0 when the show is top level (run from [start] or from show control) parent_kickback_signal - not used other than it being passed to a show """ # need to instantiate the medialist here as not using gapshow self.medialist=MediaList('ordered') Show.base_play(self,end_callback,show_ready_callback, parent_kickback_signal,level,controls_list) self.mon.trace(self,self.show_params['show-ref']) #parse the show and track timeouts reason,message,self.show_timeout=Show.calculate_duration(self,self.show_params['show-timeout']) if reason =='error': self.mon.err(self,'Show Timeout has bad time: '+self.show_params['show-timeout']) self.end('error','show timeout, bad time') reason,message,self.track_timeout=Show.calculate_duration(self,self.show_params['track-timeout']) if reason=='error': self.mon.err(self,'Track Timeout has bad time: '+self.show_params['track-timeout']) self.end('error','track timeout, bad time') # and delete eggtimer if self.previous_shower is not None: self.previous_shower.delete_eggtimer() # and display the menu self.do_menu_track() # ******************** # respond to inputs. # ******************** # exit received from another concurrent show def exit(self): self.stop_timers() Show.base_exit(self) # show timeout happened def show_timeout_stop(self): self.stop_timers() Show.base_show_timeout_stop(self) # terminate Pi Presents def terminate(self): self.stop_timers() Show.base_terminate(self) def handle_input_event(self,symbol): Show.base_handle_input_event(self,symbol) def handle_input_event_this_show(self,symbol): # menushow has only internal operation operation=self.base_lookup_control(symbol,self.controls_list) self.do_operation(operation) def do_operation(self,operation): # service the standard inputs for this show self.mon.trace(self,operation) if operation =='exit': self.exit() elif operation == 'stop': self.stop_timers() if self.current_player is not None: if self.menu_showing is True and self.level != 0: # if quiescent then set signal to stop the show when track has stopped self.user_stop_signal=True self.current_player.input_pressed('stop') elif operation in ('up','down'): # stop show timeout if self.show_timeout_timer is not None: self.canvas.after_cancel(self.show_timeout_timer) # and start it again if self.show_timeout != 0: self.show_timeout_timer=self.canvas.after(self.show_timeout*1000,self.show_timeout_stop) if operation=='up': self.previous() else: self.next() elif operation =='play': self.next_track_signal=True self.next_track=self.medialist.selected_track() self.current_player.stop() # cancel show timeout if self.show_timeout_timer is not None: self.canvas.after_cancel(self.show_timeout_timer) self.show_timeout_timer=None # stop current track (the menuplayer) if running or just start the next track if self.current_player is not None: self.current_player.input_pressed('stop') else: self.what_next_after_showing() elif operation in ('no-command','null'): return elif operation == 'pause': if self.current_player is not None: self.current_player.input_pressed(operation) elif operation[0:4]=='omx-' or operation[0:6]=='mplay-'or operation[0:5]=='uzbl-': if self.current_player is not None: self.current_player.input_pressed(operation) def next(self): # remove highlight if self.current_player.__class__.__name__ == 'MenuPlayer': self.current_player.highlight_menu_entry(self.menu_index,False) self.medialist.next('ordered') if self.menu_index==self.menu_length-1: self.menu_index=0 else: self.menu_index+=1 # and highlight the new entry self.current_player.highlight_menu_entry(self.menu_index,True) def previous(self): # remove highlight if self.current_player.__class__.__name__ == 'MenuPlayer': self.current_player.highlight_menu_entry(self.menu_index,False) if self.menu_index==0: self.menu_index=self.menu_length-1 else: self.menu_index-=1 self.medialist.previous('ordered') # and highlight the new entry self.current_player.highlight_menu_entry(self.menu_index,True) # ********************* # Sequencing # ********************* def track_timeout_callback(self): self.do_operation('stop') def do_menu_track(self): self.menu_showing=True self.mon.trace(self,'') # start show timeout alarm if required if self.show_timeout != 0: self.show_timeout_timer=self.canvas.after(self.show_timeout *1000,self.show_timeout_stop) # init the index used to hiighlight the selected menu entry by menuplayer self.menu_index=0 index = self.medialist.index_of_track(self.show_params['menu-track-ref']) if index == -1: self.mon.err(self,"'menu-track' not in medialist: " + self.show_params['menu-track-ref']) self.end('error',"menu-track not in medialist: ") return #make the medialist available to the menuplayer for cursor scrolling self.show_params['medialist_obj']=self.medialist # load the menu track self.start_load_show_loop(self.medialist.track(index)) # ********************* # Playing show or track loop # ********************* def start_load_show_loop(self,selected_track): # shuffle players Show.base_shuffle(self) self.mon.trace(self,'') self.display_eggtimer() # get control bindings for this show # needs to be done for each track as track can override the show controls if self.show_params['disable-controls'] == 'yes': self.controls_list=[] else: reason,message,self.controls_list= self.controlsmanager.get_controls(self.show_params['controls']) if reason=='error': self.mon.err(self,message) self.end('error',"error in controls") return # load the track or show Show.base_load_track_or_show(self,selected_track,self.what_next_after_load,self.end_shower,False) # track has loaded (menu or otherwise) so show it. def what_next_after_load(self,status,message): # get the calculated length of the menu for the loaded menu track if self.current_player.__class__.__name__ == 'MenuPlayer': if self.medialist.display_length()==0: self.req_next='error' self.what_next_after_showing() return self.medialist.start() self.menu_index=0 self.menu_length=self.current_player.menu_length self.current_player.highlight_menu_entry(self.menu_index,True) self.mon.trace(self,' - load complete with status: ' + status + ' message: ' + message) if self.current_player.play_state=='load-failed': self.req_next='error' self.what_next_after_showing() else: if self.show_timeout_signal is True or self.terminate_signal is True or self.exit_signal is True or self.user_stop_signal is True: self.what_next_after_showing() else: self.mon.trace(self,'') self.current_player.show(self.track_ready_callback,self.finished_showing,self.closed_after_showing) def finished_showing(self,reason,message): # showing has finished with 'pause at end', showing the next track will close it after next has started showing self.mon.trace(self,'') self.mon.log(self,"pause at end of showing track with reason: "+reason+ ' and message: '+ message) self.sr.hide_click_areas(self.controls_list) if self.current_player.play_state == 'show-failed': self.req_next = 'error' else: self.req_next='finished-player' self.what_next_after_showing() def closed_after_showing(self,reason,message): # showing has finished with closing of player but track instance is alive for hiding the x_content self.mon.trace(self,'') self.mon.log(self,"Closed after showing track with reason: "+reason+ ' and message: '+ message) self.sr.hide_click_areas(self.controls_list) if self.current_player.play_state == 'show-failed': self.req_next = 'error' else: self.req_next='closed-player' self.what_next_after_showing() # subshow or child show has ended def end_shower(self,show_id,reason,message): self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ': Returned from shower with ' + reason +' ' + message) self.sr.hide_click_areas(self.controls_list) self.req_next=reason Show.base_end_shower(self) self.what_next_after_showing() # at the end of a track check for terminations else re-display the menu def what_next_after_showing(self): self.mon.trace(self,'') # cancel track timeout timer if self.track_timeout_timer is not None: self.canvas.after_cancel(self.track_timeout_timer) self.track_timeout_timer=None # need to terminate? if self.terminate_signal is True: self.terminate_signal=False # set what to do when closed or unloaded self.ending_reason='killed' Show.base_close_or_unload(self) elif self.req_next== 'error': self.req_next='' # set what to do after closed or unloaded self.ending_reason='error' Show.base_close_or_unload(self) # show timeout elif self.show_timeout_signal is True: self.show_timeout_signal=False # set what to do when closed or unloaded self.ending_reason='show-timeout' Show.base_close_or_unload(self) # used by exit for stopping show from other shows. elif self.exit_signal is True: self.exit_signal=False self.ending_reason='exit' Show.base_close_or_unload(self) # user wants to stop elif self.user_stop_signal is True: self.user_stop_signal=False self.ending_reason='user-stop' Show.base_close_or_unload(self) elif self.next_track_signal is True: self.next_track_signal=False self.menu_showing=False # start timeout for the track if required if self.track_timeout != 0: self.track_timeout_timer=self.canvas.after(self.track_timeout*1000,self.track_timeout_callback) Show.write_stats(self,'play',self.show_params,self.next_track) self.start_load_show_loop(self.next_track) else: # no stopping the show required so re-display the menu self.do_menu_track() # ********************* # Interface with other shows/players to reduce black gaps # ********************* # called just before a track is shown to remove the previous track from the screen # and if necessary close it def track_ready_callback(self,enable_show_background): self.delete_eggtimer() if self.show_params['disable-controls'] != 'yes': #merge controls from the track controls_text=self.current_player.get_links() reason,message,track_controls=self.controlsmanager.parse_controls(controls_text) if reason == 'error': self.mon.err(self,message + " in track: "+ self.current_player.track_params['track-ref']) self.req_next='error' self.what_next_after_showing() self.controlsmanager.merge_controls(self.controls_list,track_controls) self.sr.enable_click_areas(self.controls_list) Show.base_track_ready_callback(self,enable_show_background) # callback from begining of a subshow, provide previous shower player to called show def subshow_ready_callback(self): return Show.base_subshow_ready_callback(self) # ********************* # Ending the show # ********************* def end(self,reason,message): self.stop_timers() Show.base_end(self,reason,message) def stop_timers(self): if self.track_timeout_timer is not None: self.canvas.after_cancel(self.track_timeout_timer) self.track_timeout_timer=None if self.show_timeout_timer is not None: self.canvas.after_cancel(self.show_timeout_timer) self.show_timeout_timer=None
class MediaShow: # ******************* # External interface # ******************** def __init__(self, show_params, canvas, showlist, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the dictionary fo the show to be played pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory """ self.mon = Monitor() self.mon.on() # instantiate arguments self.show_params = show_params self.showlist = showlist self.canvas = canvas self.pp_home = pp_home self.pp_profile = pp_profile # open resources self.rr = ResourceReader() # Init variables self.player = None self.shower = None self._poll_for_interval_timer = None self._poll_for_continue_timer = None self._waiting_for_interval = False self._interval_timer = None self.duration_timer = None self.error = False self._interval_timer_signal = False self._end_trigger_signal = False self._end_mediashow_signal = False self._next_track_signal = False self._previous_track_signal = False self._play_child_signal = False self._req_next = "nil" # create and instance of TimeOfDay scheduler so we can add events self.tod = TimeOfDay() self._state = "closed" def play(self, show_id, end_callback, ready_callback=None, top=False, command="nil"): """ displays the mediashow end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ # instantiate the arguments self.show_id = show_id self._end_callback = end_callback self._ready_callback = ready_callback self.top = top self.command = command self.mon.log(self, "Starting show: Id= " + str(self.show_id) + " " + self.show_params["show-ref"]) # check data files are available. self.media_file = self.pp_profile + "/" + self.show_params["medialist"] if not os.path.exists(self.media_file): self.mon.err(self, "Medialist file not found: " + self.media_file) self._end("error", "Medialist file not found") # create a medialist for the mediashow and read it. self.medialist = MediaList() if self.medialist.open_list(self.media_file, self.showlist.sissue()) == False: self.mon.err(self, "Version of medialist different to Pi Presents") self._end("error", "Version of medialist different to Pi Presents") # set up the time of day triggers for the show if self.show_params["trigger"] in ("time", "time-quiet"): error_text = self.tod.add_times( self.show_params["trigger-input"], id(self), self.tod_start_callback, self.show_params["trigger"] ) if error_text <> "": self.mon.err(self, error_text) self._end("error", error_text) if self.show_params["trigger-end"] == "time": # print self.show_params['trigger-end-time'] error_text = self.tod.add_times( self.show_params["trigger-end-time"], id(self), self.tod_end_callback, "n/a" ) if error_text <> "": self.mon.err(self, error_text) self._end("error", error_text) if self.show_params["trigger-end"] == "duration": error_text = self.calculate_duration(self.show_params["trigger-end-time"]) if error_text <> "": self.mon.err(self, error_text) self._end("error", error_text) self._state = "closed" self.egg_timer = None self._wait_for_trigger() def calculate_duration(self, line): fields = line.split(":") if len(fields) == 1: secs = fields[0] minutes = "0" hours = "0" if len(fields) == 2: secs = fields[1] minutes = fields[0] hours = "0" if len(fields) == 3: secs = fields[2] minutes = fields[1] hours = fields[0] self.duration = 3600 * long(hours) + 60 * long(minutes) + long(secs) return "" # ******************************** # Respond to external events # ******************************** # respond to key presses def key_pressed(self, key_name): self.mon.log(self, "received key: " + key_name) if self.show_params["disable-controls"] == "yes": return if key_name == "": pass elif key_name == "escape": # if next lower show is running pass down to stop the show and lower level if self.shower <> None: self.shower.key_pressed(key_name) # if not at top stop the show else: if self.top == False: self._end_mediashow_signal = True # and if a track is running stop that first if self.player <> None: self.player.key_pressed(key_name) else: # at top level in a manual presentation stop the track if self.show_params["progress"] == "manual": if self.player <> None: self.player.key_pressed(key_name) elif key_name in ("up", "down"): # if child or sub-show is running and is a show pass to show, track does not use up/down # otherwise use keys for next or previous if self.shower <> None: self.shower.key_pressed(key_name) else: if key_name == "up" and self._state == "playing": self._previous() else: self._next() elif key_name == "return": # if child show or sub-show is running and is show - pass down- player does not use return # ELSE use Return to start child or to start the show if waiting if self.shower <> None: self.shower.key_pressed(key_name) else: if self._state == "playing": if self.show_params["has-child"] == "yes": self._play_child_signal = True # and stop the current track if its running if self.player <> None: self.player.key_pressed("escape") else: if self._state == "waiting": self._start_show() elif key_name in ("p", " "): # pass down if show or track running. if self.shower <> None: self.shower.key_pressed(key_name) elif self.player <> None: self.player.key_pressed(key_name) def button_pressed(self, button, edge): # print 'mediashow button pressed', button if button == "play": self.key_pressed("return") elif button == "up": self.key_pressed("up") elif button == "down": self.key_pressed("down") elif button == "stop": self.key_pressed("escape") elif button == "pause": self.key_pressed("p") else: self.input_pressed(button) def input_pressed(self, xinput): # print self._state, self.show_params['trigger-next'], self.show_params['next-input'] if ( self._state == "waiting" and self.show_params["trigger"] == "GPIO" and xinput == self.show_params["trigger-input"] ): self.key_pressed("return") elif ( self._state == "playing" and self.show_params["trigger-next"] == "GPIO" and xinput == self.show_params["next-input"] ): self.key_pressed("down") # callback from time of day scheduler def tod_start_callback(self): if self._state == "waiting" and self.show_params["trigger"] in ("time", "time-quiet"): self._start_show() def tod_end_callback(self): if self._state == "playing" and self.show_params["trigger-end"] in ("time", "duration"): self._end_trigger_signal = True if self.shower <> None: self.shower.key_pressed("escape") elif self.player <> None: self.player.key_pressed("escape") # kill or error def terminate(self, reason): if self.shower <> None: self.mon.log(self, "sent terminate to shower") self.shower.terminate(reason) elif self.player <> None: self.mon.log(self, "sent terminate to player") self.player.terminate(reason) else: self._end(reason, "terminated without terminating shower or player") def _tidy_up(self): # clear outstanding time of day events for this show self.tod.clear_times_list(id(self)) if self._poll_for_continue_timer <> None: self.canvas.after_cancel(self._poll_for_continue_timer) self._poll_for_continue_timer = None if self._poll_for_interval_timer <> None: self.canvas.after_cancel(self._poll_for_interval_timer) self._poll_for_interval_timer = None if self._interval_timer <> None: self.canvas.after_cancel(self._interval_timer) self._interval_timer = None if self.duration_timer <> None: self.canvas.after_cancel(self.duration_timer) self.duration_timer = None def resource(self, section, item): value = self.rr.get(section, item) if value == False: self.mon.err(self, "resource: " + section + ": " + item + " not found") self.terminate("error") else: return value # *************************** # Do actions as a result of events # *************************** def _stop(self, message): self._end_mediashow_signal = True if self._interval_timer <> None: self.canvas.after_cancel(self._interval_timer) def _next(self): # stop track if running and set signal self._next_track_signal = True if self.shower <> None: self.shower.key_pressed("escape") else: if self.player <> None: self.player.key_pressed("escape") def _previous(self): self._previous_track_signal = True if self.shower <> None: self.shower.key_pressed("escape") else: if self.player <> None: self.player.key_pressed("escape") # *************************** # end of show functions # *************************** def _end(self, reason, message): self._end_mediashow_signal = False self.mon.log(self, "Ending Mediashow: " + self.show_params["show-ref"]) self._tidy_up() self._end_callback(self.show_id, reason, message) self = None return # *************************** # Show sequencer # *************************** # wait for trigger sets the state to waiting so that key/button presses can do a start show. def _wait_for_trigger(self): self._state = "waiting" if self.ready_callback <> None: self.ready_callback() self.mon.log(self, "Waiting for trigger: " + self.show_params["trigger"]) if self.show_params["trigger"] == "button": # blank screen waiting for trigger if auto, otherwise display something if self.show_params["progress"] == "manual": text = self.resource("mediashow", "m01") else: text = "" self.display_message(self.canvas, "text", text, 0, self._start_show) elif self.show_params["trigger"] == "GPIO": # blank screen waiting for trigger # text = self.resource('mediashow','m02') # self.display_message(self.canvas,'text',text,0,self._start_show) pass elif self.show_params["trigger"] in ("time", "time-quiet"): # show next show notice quiet = 3 # if next show is this one display text next_show = self.tod.next_event_time() if next_show[quiet] == False: if next_show[1] == "tomorrow": text = self.resource("mediashow", "m09") else: text = self.resource("mediashow", "m08") text = text.replace("%tt", next_show[0]) self.display_message(self.canvas, "text", text, 0, self._start_show) elif self.show_params["trigger"] == "start": self._start_show() else: self.mon.err(self, "Unknown trigger: " + self.show_params["trigger"]) self._end("error", "Unknown trigger type") def _start_show(self): self._state = "playing" self._direction = "forward" # self.canvas.delete(ALL) # start interval timer if self.show_params["repeat"] == "interval" and self.show_params["repeat-interval"] <> 0: self._interval_timer_signal = False self._interval_timer = self.canvas.after( int(self.show_params["repeat-interval"]) * 1000, self._end_interval_timer ) # start duration timer if self.show_params["trigger-end"] == "duration": # print 'set alarm ', self.duration self.duration_timer = self.canvas.after(self.duration * 1000, self.tod_end_callback) # and play the first track unless commanded otherwise if self.command == "backward": self.medialist.finish() else: self.medialist.start() self._play_selected_track(self.medialist.selected_track()) def _what_next(self): self._direction = "forward" # end of show trigger if self._end_trigger_signal == True: self._end_trigger_signal = False if self.top == True: self._state = "waiting" self._wait_for_trigger() else: # not at top so stop the show self._end("normal", "sub-show end time trigger") # user wants to end, wait for any shows or tracks to have ended then end show elif self._end_mediashow_signal == True: if self.player == None and self.shower == None: self._end_mediashow_signal = False self._end("normal", "show ended by user") else: pass # returning from a subshow needing to move onward elif self._req_next == "do-next": self._req_next = "nil" self.medialist.next(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) # returning from a subshow needing to move backward elif self._req_next == "do-previous": self._req_next = "nil" self._direction = "backward" self.medialist.previous(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) # user wants to play child elif self._play_child_signal == True: self._play_child_signal = False index = self.medialist.index_of_track("pp-child-show") if index >= 0: # don't select the track as need to preserve mediashow sequence. child_track = self.medialist.track(index) self._display_eggtimer(self.resource("mediashow", "m07")) self._play_selected_track(child_track) else: self.mon.err(self, "Child show not found in medialist: " + self.show_params["pp-child-show"]) self._end("error", "child show not found in medialist") # skip to next track on user input elif self._next_track_signal == True: self._next_track_signal = False if self.medialist.at_end() == True: if ( self.show_params["sequence"] == "ordered" and self.show_params["repeat"] == "oneshot" and self.top == False ): self._end("do-next", "Return from Sub Show") else: self.medialist.next(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) else: self.medialist.next(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) # skip to previous track on user input elif self._previous_track_signal == True: self._previous_track_signal = False self._direction = "backward" if self.medialist.at_start() == True: if ( self.show_params["sequence"] == "ordered" and self.show_params["repeat"] == "oneshot" and self.top == False ): self._end("do-previous", "Return from Sub Show") else: self.medialist.previous(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) else: self.medialist.previous(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) # track is finished and we are on auto elif self.show_params["progress"] == "auto": if self.medialist.at_end() == True: if ( self.show_params["sequence"] == "ordered" and self.show_params["repeat"] == "oneshot" and self.top == False ): self._end("do-next", "Return from Sub Show") elif ( self.show_params["sequence"] == "ordered" and self.show_params["repeat"] == "oneshot" and self.top == True ): self._wait_for_trigger() elif self._waiting_for_interval == True: if self._interval_timer_signal == True: self._interval_timer_signal = False self._waiting_for_interval = False self._start_show() else: self._poll_for_interval_timer = self.canvas.after(1000, self._what_next) elif ( self.show_params["sequence"] == "ordered" and self.show_params["repeat"] == "interval" and int(self.show_params["repeat-interval"]) > 0 ): self._waiting_for_interval = True self._poll_for_interval_timer = self.canvas.after(1000, self._what_next) # elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])==0: elif self.show_params["repeat"] == "interval" and int(self.show_params["repeat-interval"]) == 0: self.medialist.next(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) # shffling so there is no end condition elif self.show_params["sequence"] == "shuffle": self.medialist.next(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) else: self.mon.err( self, "Unhandled playing event: " + self.show_params["sequence"] + " with " + self.show_params["repeat"] + " of " + self.show_params["repeat-interval"], ) self._end("error", "Unhandled playing event") else: self.medialist.next(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) # track has finished and we are on manual progress elif self.show_params["progress"] == "manual": self._delete_eggtimer() self.canvas.delete(ALL) if self.show_params["trigger-next"] == "button": self._display_eggtimer(self.resource("mediashow", "m03")) self._poll_for_continue_timer = self.canvas.after(2000, self._what_next) else: # unhandled state self.mon.err(self, "Unhandled playing event: ") self._end("error", "Unhandled playing event") def _end_interval_timer(self): self._interval_timer_signal = True # *************************** # Dispatching to Players/Shows # *************************** # used to display internal messages in situations where a medialist entry could be used. def display_message(self, canvas, source, content, duration, _display_message_callback): self._display_message_callback = _display_message_callback tp = { "duration": duration, "message-colour": "white", "message-font": "Helvetica 20 bold", "background-colour": "", "background-image": "", } self.player = MessagePlayer(self.show_id, canvas, self.pp_home, tp, tp) self.player.play(content, self._display_message_end, None) def _display_message_end(self, reason, message): self.player = None if reason in ("error", "killed"): self._end(reason, message) else: self._display_message_callback() def complete_path(self, selected_track): # complete path of the filename of the selected entry track_file = selected_track["location"] if track_file <> "" and track_file[0] == "+": track_file = self.pp_home + track_file[1:] self.mon.log(self, "Track to play is: " + track_file) return track_file def _play_selected_track(self, selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ # self.canvas.delete(ALL) if self.show_params["progress"] == "manual": self._display_eggtimer(self.resource("mediashow", "m04")) # is menu required if self.show_params["has-child"] == "yes": enable_child = True else: enable_child = False # dispatch track by type self.player = None self.shower = None track_type = selected_track["type"] self.mon.log(self, "Track type is: " + track_type) if track_type == "video": # create a videoplayer track_file = self.complete_path(selected_track) self.player = VideoPlayer(self.show_id, self.canvas, self.pp_home, self.show_params, selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "audio": # create a audioplayer track_file = self.complete_path(selected_track) self.player = AudioPlayer(self.show_id, self.canvas, self.pp_home, self.show_params, selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "image": track_file = self.complete_path(selected_track) # images played from menus don't have children self.player = ImagePlayer(self.show_id, self.canvas, self.pp_home, self.show_params, selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "message": # bit odd because MessagePlayer is used internally to display text. text = selected_track["text"] self.player = MessagePlayer(self.show_id, self.canvas, self.pp_home, self.show_params, selected_track) self.player.play(text, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "show": # get the show from the showlist index = self.showlist.index_of_show(selected_track["sub-show"]) if index >= 0: self.showlist.select(index) selected_show = self.showlist.selected_show() else: self.mon.err(self, "Show not found in showlist: " + selected_track["sub-show"]) self._end("error", "Unknown show") if selected_show["type"] == "mediashow": self.shower = MediaShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, top=False, command=self._direction) elif selected_show["type"] == "liveshow": self.shower = LiveShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, top=False, command="nil") elif selected_show["type"] == "menu": self.shower = MenuShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, top=False, command="nil") else: self.mon.err(self, "Unknown Show Type: " + selected_show["type"]) self._end("error" "Unknown show type") else: self.mon.err(self, "Unknown Track Type: " + track_type) self._end("error", "Unknown track type") def ready_callback(self): self._delete_eggtimer() def end_player(self, reason, message): self._req_next = "nil" self.mon.log(self, " Show Id: " + str(self.show_id) + " Returned from player with message: " + message) self.player = None if reason in ("killed", "error"): self._end(reason, message) elif self.show_params["progress"] == "manual": self._display_eggtimer(self.resource("mediashow", "m05")) self._req_next = reason self._what_next() else: self._req_next = reason self._what_next() def end_shower(self, show_id, reason, message): self._req_next = "nil" self.mon.log(self, "Returned from shower with message: " + message) self.shower = None if reason in ("killed", "error"): self._end(reason, message) elif self.show_params["progress"] == "manual": self._display_eggtimer(self.resource("mediashow", "m06")) self._req_next = reason self._what_next() else: self._req_next = reason self._what_next() def _display_eggtimer(self, text): self.egg_timer = self.canvas.create_text( int(self.canvas["width"]) / 2, int(self.canvas["height"]) / 2, text=text, fill="white", font="Helvetica 20 bold", ) self.canvas.update_idletasks() def _delete_eggtimer(self): if self.egg_timer != None: self.canvas.delete(self.egg_timer) self.canvas.update_idletasks()