diff --git a/pipresents.py b/pipresents.py index 3f11d93..7a3ce43 100644 --- a/pipresents.py +++ b/pipresents.py @@ -305,6 +305,17 @@ def __init__(self): self.animate.poll() # Create list of start shows initialise them and then run them + show_id=-1 + self.show_manager=ShowManager(show_id,self.showlist,self.starter_show,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home) + + # first time through so register shows and set callback to terminate Pi Presents if all shows have ended. + self.show_manager.init(self.canvas,self.all_shows_ended_callback,self.handle_command,self.showlist) + reason,message=self.show_manager.register_shows() + if reason == 'error': + self.mon.err(self,message) + self.end('error',message) + + # and run the start shows self.run_start_shows() # kick off the time of day scheduler which may run additional shows @@ -331,16 +342,13 @@ def parse_screen(self,size_text): # ******************** def run_start_shows(self): self.mon.trace(self,'run start shows') - # start show manager - show_id=-1 #start show - self.show_manager=ShowManager(show_id,self.showlist,self.starter_show,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home) - # first time through so empty show register and set callback to terminate Pi Presents if all shows have ended. - self.show_manager.init(self.canvas,self.all_shows_ended_callback,self.handle_command) - # parse the start shows field and start the initial shows show_refs=self.starter_show['start-show'].split() for show_ref in show_refs: reason,message=self.show_manager.control_a_show(show_ref,'open') + if reason == 'error': + self.mon.err(self,message) + # ********************* @@ -358,12 +366,14 @@ def handle_command(self,command_text): self.mon.log(self,"command received: " + command_text) if command_text.strip()=="": return + fields= command_text.split() show_command=fields[0] if len(fields)>1: show_ref=fields[1] else: show_ref='' + if show_command in ('open','close'): reason,message=self.show_manager.control_a_show(show_ref,show_command) @@ -378,9 +388,10 @@ def handle_command(self,command_text): else: reason='error' message='command not recognised '+ command_text + if reason=='error': self.mon.err(self,message) - self.end(reason,message) + return def handle_output_event(self,symbol,param_type,param_values,req_time): @@ -430,6 +441,7 @@ def terminate(self): self.mon.log(self, "terminate received from user") needs_termination=False for show in self.show_manager.shows: + # print show[ShowManager.SHOW_OBJ], show[ShowManager.SHOW_REF] if show[ShowManager.SHOW_OBJ] is not None: needs_termination=True self.mon.log(self,"Sent terminate to show "+ show[ShowManager.SHOW_REF]) @@ -515,10 +527,10 @@ def tidy_up(self): if __name__ == '__main__': pp = PiPresents() - ## try: - ## pp = PiPresents() - ## except: - ## traceback.print_exc(file=open("/home/pi/pp_exceptions.log","w")) - ## pass + # try: + # pp = PiPresents() + # except: + # traceback.print_exc(file=open("/home/pi/pp_exceptions.log","w")) + # pass diff --git a/pp_animate.py b/pp_animate.py index 9c37d6b..3cc94c1 100644 --- a/pp_animate.py +++ b/pp_animate.py @@ -172,36 +172,53 @@ def clear_events_list(self,tag): Animate.events=[] + # [delay],symbol,type,state def parse_animate_fields(self,line): fields= line.split() if len(fields) == 0: return 'normal','no fields','','',[],0 - name=fields[0] - - if len(fields) not in (3,4): + elif len(fields) not in (3,4): return 'error','Wrong number of fields in : '+ line,'','',[],0 - param_type=fields[1] + elif len(fields)== 3: + + name=fields[0] + param_type=fields[1] + delay_text='0' + start_params=2 + + elif len(fields)== 4: + delay_text=fields[0] + name=fields[1] + param_type=fields[2] + start_params=3 + + else: + return 'error','Wrong number of fields in : '+ line,'','',[],0 + + # check each field + if not delay_text.isdigit(): + return 'error','Delay is not an integer in : '+ line,'','',[],0 + else: + delay=int(delay_text) + + + #only one param type at the moment. if param_type != 'state': return 'error','uknnown parameter type in : '+ line,'','',[],0 else: params_length = 1 params_check = ('on','off') + params=[] - for index in range(2, 2+params_length): + for index in range(start_params, start_params+ params_length): param=fields[index] if not param in params_check: - return 'error','unknown paramter value in : '+ line,'','',[],0 + return 'error','unknown parameter value in : '+ line,'','',[],0 params.append(param) - if len(fields) == 2+params_length: - delay_text='0' - else: - delay_text=fields[2+params_length] - - if not delay_text.isdigit(): - return 'error','Delay is not an integer in : '+ line,'','','off',0 - delay=int(delay_text) return 'normal','event parsed OK',name,param_type,params,delay + + diff --git a/pp_artliveshow.py b/pp_artliveshow.py index be43674..9bd4ca2 100644 --- a/pp_artliveshow.py +++ b/pp_artliveshow.py @@ -29,10 +29,13 @@ def __init__(self, pp_profile, command_callback) - # get the live tracks directories self.options=command_options() + def play(self,end_callback,show_ready_callback, direction_command,level,controls_list): + + self.end_callback=end_callback + # get the livetracks directories if self.show_params['live-tracks-dir1'] != '': self.pp_live_dir1= self.show_params['live-tracks-dir1'] @@ -80,9 +83,9 @@ def __init__(self, # use the appropriate medialist - self.medialist=LiveList(show_params['sequence']) + self.medialist=LiveList(self.show_params['sequence']) # and pass directories to Livelist self.medialist.live_tracks(self.pp_live_dir1,self.pp_live_dir2) - + ArtShow.play(self,end_callback,show_ready_callback, direction_command,level,controls_list) diff --git a/pp_artmediashow.py b/pp_artmediashow.py index 68e1d45..43c04d1 100644 --- a/pp_artmediashow.py +++ b/pp_artmediashow.py @@ -28,9 +28,10 @@ def __init__(self, command_callback) + def play(self,end_callback,show_ready_callback, direction_command,level,controls_list): + # use the appropriate medialist - self.medialist=MediaList(show_params['sequence']) + self.medialist=MediaList(self.show_params['sequence']) - - + ArtShow.play(self,end_callback,show_ready_callback, direction_command,level,controls_list) diff --git a/pp_artshow.py b/pp_artshow.py index 3871e6f..95795cb 100644 --- a/pp_artshow.py +++ b/pp_artshow.py @@ -168,7 +168,7 @@ def start_show(self): def load_first_track(self): self.mon.trace(self,'') if self.medialist.start() is False: - # list is empty - display a message for 10 secs and then retry + # list is empty - display a message for 5 secs and then retry Show.display_admin_message(self,Show.base_resource(self,'mediashow','m11')) self.canvas.after(5000,self.remove_list_empty_message) else: @@ -268,7 +268,6 @@ def what_next(self): else: # otherwise show the next track - print 'show next track after end' self.show_next_track() else: # otherwise show the next track @@ -280,7 +279,7 @@ def show_next_track(self): self.previous_player=self.current_player self.current_player=self.next_player self.next_player=None - self.mon.trace(self,'AFTER SHUFFLE n-c-p' + self.next_player + ' ' + self.current_player + ' ' + self.previous_player) + self.mon.trace(self,'AFTER SHUFFLE n-c-p' + str(self.next_player) + ' ' + str(self.current_player) + ' ' + str(self.previous_player)) self.mon.trace(self, 'showing track') if self.end_medialist_warning is True: self.end_medialist_signal = True @@ -369,7 +368,7 @@ def close_current_and_next(self): def end_close_current(self,reason,message): - self.mon.log(self,"end_close_current - Current closed with reason: "+ reason + ' and message: '+ message) + self.mon.log(self,"Current track closed with reason: "+ reason + ' and message: '+ message) self.mon.trace(self,' - current closed') self.current_player=None # safer to delete the player here rather than in player as play-state is read elsewhere. @@ -412,14 +411,14 @@ def wait_for_end(self): self.mon.err(self,"Unhandled ending_reason: ") self.end('error',"Unhandled ending_reason") - def track_ready_callback(self): + def track_ready_callback(self,enable_show_background): self.mon.trace(self, '') # close the player from the previous track if self.previous_player is not None: - self.mon.trace(self, 'hiding previous: ' + self.previous_player) + self.mon.trace(self, 'hiding previous: ' + str(self.previous_player)) self.previous_player.hide() if self.previous_player.get_play_state() == 'showing': - self.mon.trace(self,'closing previous: ' + self.previous_player) + self.mon.trace(self,'closing previous: ' + str(self.previous_player)) self.previous_player.close(self.closed_callback) else: self.mon.trace(self, 'previous is none') diff --git a/pp_controlsmanager.py b/pp_controlsmanager.py index dad0f87..c563d23 100644 --- a/pp_controlsmanager.py +++ b/pp_controlsmanager.py @@ -23,6 +23,20 @@ def get_controls(self,controls_text): controls_list.append([control[0],control[1]]) return 'normal','controls read',controls_list + def merge_controls(self,current_controls,track_controls): + for track_control in track_controls: + for control in current_controls: + if track_control[0] == control[0]: + # link exists so overwrite + control[1]=track_control[1] + break + else: + # new link so append it + current_controls.append([track_control[0],track_control[1]]) + # print "\n merging" + # print current_controls + + # parse controls from controls field in a show def parse_controls(self,controls_text): @@ -45,7 +59,7 @@ def parse_control(self,line): return "incorrect number of fields in control "+line,['',''] symbol=fields[0] operation=fields[1] - if operation in ('stop','play','up','down','pause','exit') or operation[0:4] == 'omx-' or operation[0:6] == 'mplay-'or operation[0:5] == 'uzbl-': + if operation in ('stop','play','up','down','pause','exit','null','no-command') or operation[0:4] == 'omx-' or operation[0:6] == 'mplay-'or operation[0:5] == 'uzbl-': return '',[symbol,operation] else: return "controls, unknown operation in\n "+ line,['',''] diff --git a/pp_definitions.py b/pp_definitions.py index 10dc820..d8e885b 100644 --- a/pp_definitions.py +++ b/pp_definitions.py @@ -48,7 +48,7 @@ class PPdefinitions(object): 'background-image','background-colour','show-text','show-text-font','show-text-colour','show-text-x','show-text-y', 'tab-tracks','sep', 'transition', 'duration','image-window','audio-speaker','mplayer-audio','mplayer-volume','mplayer-other-options', - 'omx-audio','omx-volume','omx-window','omx-other-options','web-window','freeze-at-end', + 'omx-audio','omx-volume','omx-window','omx-other-options','freeze-at-end','web-window', 'tab-controls','sep', 'disable-controls', 'controls' ], @@ -68,7 +68,7 @@ class PPdefinitions(object): 'background-image','background-colour','show-text','show-text-font','show-text-colour','show-text-x','show-text-y', 'tab-tracks','sep', 'transition','duration','image-window','audio-speaker','mplayer-audio','mplayer-volume','mplayer-other-options', - 'omx-audio','omx-volume','omx-window','omx-other-options','web-window','freeze-at-end', + 'omx-audio','omx-volume','omx-window','omx-other-options','freeze-at-end','web-window', 'tab-controls','sep', 'disable-controls','controls' ], @@ -85,7 +85,7 @@ class PPdefinitions(object): 'background-image','background-colour','show-text','show-text-font','show-text-colour','show-text-x','show-text-y', 'tab-tracks','sep', 'transition', 'duration','image-window','audio-speaker','mplayer-audio', - 'mplayer-volume','mplayer-other-options','omx-audio','omx-volume','omx-window','omx-other-options','web-window','freeze-at-end', + 'mplayer-volume','mplayer-other-options','omx-audio','omx-volume','omx-window','omx-other-options','freeze-at-end','web-window', 'tab-controls','sep', 'disable-controls','controls' ], @@ -100,7 +100,7 @@ class PPdefinitions(object): 'background-image','background-colour','show-text','show-text-font','show-text-colour','show-text-x','show-text-y', 'tab-tracks','sep', 'transition', 'duration','image-window','audio-speaker','mplayer-audio','mplayer-volume','mplayer-other-options', - 'omx-audio','omx-volume','omx-window','omx-other-options','web-window','freeze-at-end', + 'omx-audio','omx-volume','omx-window','omx-other-options','freeze-at-end','web-window', 'tab-links','sep', 'links' ], @@ -115,7 +115,7 @@ class PPdefinitions(object): 'background-image','background-colour','show-text','show-text-font','show-text-colour','show-text-x','show-text-y', 'tab-tracks','sep', 'transition', 'duration','image-window','audio-speaker','mplayer-audio','mplayer-volume','mplayer-other-options', - 'omx-audio','omx-volume','omx-window','omx-other-options','web-window','freeze-at-end', + 'omx-audio','omx-volume','omx-window','omx-other-options','freeze-at-end','web-window', 'tab-links','sep', 'links' ], @@ -131,69 +131,71 @@ class PPdefinitions(object): new_shows={ 'artliveshow':{'title': 'New ArtLiveshow','show-ref':'','show-canvas':'', 'type': 'artliveshow', 'disable-controls':'no','sequence': 'ordered','repeat':'repeat','medialist': '', - 'show-text':'','show-text-font':'','show-text-colour':'','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', + 'show-text':'','show-text-font':'Helvetica 20 bold','show-text-colour':'white','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', 'eggtimer-x':'100','eggtimer-y':'100','eggtimer-font':'Helvetica 10 bold','eggtimer-colour':'white', 'transition': 'cut', 'duration': '5','image-window':'original','audio-speaker':'stereo','mplayer-audio':'hdmi','mplayer-volume':'0','mplayer-other-options':'', - 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'original','omx-other-options': '','freeze-at-end':'yes', - 'controls':'','live-tracks-dir1':'','live-tracks-dir2':''}, + 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'warp 300 300 655 500','omx-other-options': '','freeze-at-end':'yes', + 'controls':'','live-tracks-dir1':'','live-tracks-dir2':'', + 'controls':'pp-down down\npp-up up\npp-stop stop\npp-pause pause\npp-exit exit\n'}, 'artmediashow':{'title': 'New ArtMediashow','show-ref':'','show-canvas':'', 'type': 'artmediashow', 'disable-controls':'no','sequence': 'ordered','repeat':'repeat','medialist': '', - 'show-text':'','show-text-font':'','show-text-colour':'','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', + 'show-text':'','show-text-font':'Helvetica 20 bold','show-text-colour':'white','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', 'eggtimer-x':'100','eggtimer-y':'100','eggtimer-font':'Helvetica 10 bold','eggtimer-colour':'white', 'transition': 'cut', 'duration': '5','image-window':'original','audio-speaker':'stereo','mplayer-audio':'hdmi','mplayer-volume':'0','mplayer-other-options':'', - 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'original','omx-other-options': '','freeze-at-end':'yes', - 'controls':''}, + 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'warp 300 300 655 500','omx-other-options': '','freeze-at-end':'yes', + 'controls':'pp-down down\npp-up up\npp-stop stop\npp-pause pause\npp-exit exit\n'}, 'hyperlinkshow':{ 'type':'hyperlinkshow','title':'New Hyperlink Show','show-ref':'', 'show-canvas':'', 'medialist':'', 'links':'','first-track-ref':'','home-track-ref':'','timeout-track-ref':'','disable-controls':'no','show-timeout': '60','track-timeout': '0', - 'show-text':'','show-text-font':'','show-text-colour':'','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', + 'show-text':'','show-text-font':'Helvetica 20 bold','show-text-colour':'white','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', 'eggtimer-x':'100','eggtimer-y':'100','eggtimer-font':'Helvetica 10 bold','eggtimer-colour':'white', 'transition': 'cut', 'duration': '0','image-window':'original', 'audio-speaker':'stereo','mplayer-audio':'hdmi','mplayer-volume':'0','mplayer-other-options':'', - 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'original','omx-other-options': '','web-window':'warp','freeze-at-end':'yes' + 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'warp 300 300 655 500','omx-other-options': '','web-window':'warp 300 300 700 700','freeze-at-end':'yes' }, 'radiobuttonshow':{ 'type':'radiobuttonshow','title':'New Radio Button Show','show-ref':'', 'show-canvas':'', 'medialist':'', 'links':'','first-track-ref':'','disable-controls':'no','show-timeout': '60','track-timeout': '0', - 'show-text':'','show-text-font':'','show-text-colour':'','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', + 'show-text':'','show-text-font':'Helvetica 20 bold','show-text-colour':'white','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', 'eggtimer-x':'100','eggtimer-y':'100','eggtimer-font':'Helvetica 10 bold','eggtimer-colour':'white', 'transition': 'cut', 'duration': '0','image-window':'original', 'audio-speaker':'stereo','mplayer-audio':'hdmi','mplayer-volume':'0','mplayer-other-options':'', - 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'original','omx-other-options': '','web-window':'warp','freeze-at-end':'yes' + 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'warp 300 300 655 500','omx-other-options': '','web-window':'warp 300 300 700 700','freeze-at-end':'yes' }, 'mediashow':{'title': 'New Mediashow','show-ref':'', 'show-canvas':'', 'type': 'mediashow','medialist': '', 'disable-controls':'no','trigger-start-type': 'start','trigger-start-param':'','trigger-next-type': 'continue','trigger-next-param':'','sequence': 'ordered','repeat': 'repeat','trigger-end-type':'none', 'trigger-end-param':'', 'child-track-ref': '', 'hint-text': '', 'hint-x':'200','hint-y': '980','hint-font': 'Helvetica 30 bold','hint-colour': 'white', 'eggtimer-x':'100','eggtimer-y':'100','eggtimer-font':'Helvetica 10 bold','eggtimer-colour':'white', - 'show-text':'','show-text-font':'','show-text-colour':'','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', - 'transition': 'cut', 'duration': '5','image-window':'original','audio-speaker':'stereo','mplayer-audio':'hdmi','mplayer-volume':'0','mplayer-other-options':'','web-window':'warp', - 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'original','omx-other-options': '','freeze-at-end':'yes', + 'show-text':'','show-text-font':'Helvetica 20 bold','show-text-colour':'white','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', + 'transition': 'cut', 'duration': '5','image-window':'original','audio-speaker':'stereo','mplayer-audio':'hdmi','mplayer-volume':'0','mplayer-other-options':'','web-window':'warp 300 300 700 700', + 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'warp 300 300 655 500','omx-other-options': '','freeze-at-end':'yes', 'controls':'pp-down down\npp-up up\npp-play play\npp-stop stop\npp-pause pause\npp-exit exit\n'}, 'liveshow':{'title': 'New Liveshow','show-ref':'','show-canvas':'', 'type': 'liveshow', 'disable-controls':'no','trigger-start-type':'start','trigger-start-param':'','trigger-next-type': 'continue','trigger-next-param':'', 'sequence': 'ordered','repeat': 'repeat','trigger-end-type': 'none', 'trigger-end-param':'','medialist': '', 'child-track-ref': '', 'hint-text': '','hint-x':'200', 'hint-y': '980','hint-font': 'Helvetica 30 bold','hint-colour': 'white', - 'show-text':'','show-text-font':'','show-text-colour':'','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', + 'show-text':'','show-text-font':'Helvetica 20 bold','show-text-colour':'white','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', 'eggtimer-x':'100','eggtimer-y':'100','eggtimer-font':'Helvetica 10 bold','eggtimer-colour':'white', 'transition': 'cut', 'duration': '5','image-window':'original','audio-speaker':'stereo','mplayer-audio':'hdmi','mplayer-volume':'0','mplayer-other-options':'', - 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'original','omx-other-options': '','web-window':'warp','freeze-at-end':'yes', - 'controls':'','live-tracks-dir1':'','live-tracks-dir2':''}, + 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'warp 300 300 655 500','omx-other-options': '','web-window':'warp 300 300 700 700','freeze-at-end':'yes', + 'live-tracks-dir1':'','live-tracks-dir2':'', + 'controls':'pp-down down\npp-up up\npp-play play\npp-stop stop\npp-pause pause\npp-exit exit\n'}, 'menu':{'show-ref': '','show-canvas':'', 'title': 'New Menu','type': 'menu','medialist': '', 'disable-controls':'no','show-timeout': '0','track-timeout':'0','menu-background-colour':'black','menu-background-image':'', 'entry-font': 'Helvetica 30 bold','entry-colour': 'white', 'entry-select-colour': 'red', 'hint-text': 'Up, down to Select, Return to Play', 'hint-x':'200','hint-y': '980', 'hint-font': 'Helvetica 30 bold', 'hint-colour': 'white', - 'menu-text': '', 'menu-text-x':'','menu-text-y': '', 'menu-text-font': '', 'menu-text-colour': '', + 'menu-text': '', 'menu-text-x':'200','menu-text-y': '20', 'menu-text-font': 'Helvetica 30 bold', 'menu-text-colour': 'white', 'menu-window':'300 250','menu-direction':'vertical','menu-rows':'10','menu-columns':'1','menu-icon-mode':'bullet','menu-text-mode':'right', 'menu-bullet':'/home/pi/pipresents/pp_home/pp_resources/bullet_square_grey.png', 'menu-icon-width':'80','menu-icon-height':'80', 'menu-horizontal-padding':'10','menu-vertical-padding':'10','menu-text-width':'800','menu-text-height':'50', 'menu-horizontal-separation':'20','menu-vertical-separation':'20','menu-strip':'no','menu-strip-padding':'5','menu-guidelines':'never', 'eggtimer-x':'100','eggtimer-y':'100','eggtimer-font':'Helvetica 10 bold','eggtimer-colour':'white', - 'show-text':'','show-text-font':'','show-text-colour':'','show-text-x':'0','show-text-y':'0','background-image':'','background-colour':'', + 'show-text':'','show-text-font':'Helvetica 20 bold','show-text-colour':'white','show-text-x':'100','show-text-y':'50','background-image':'','background-colour':'', 'transition': 'cut', 'duration': '5','image-window':'original','audio-speaker':'stereo','mplayer-audio':'hdmi','mplayer-volume':'0', 'mplayer-other-options':'', - 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'original','omx-other-options': '','web-window':'warp','freeze-at-end':'yes', + 'omx-audio': 'hdmi','omx-volume':'0','omx-window':'warp 300 300 655 500','omx-other-options': '','web-window':'warp 300 300 700 700','freeze-at-end':'yes', 'controls':'pp-down down\npp-up up\npp-play play\npp-stop stop\npp-pause pause\npp-exit exit\n'}, 'start':{'title': 'Start','show-ref':'start', 'type': 'start','start-show':''} @@ -294,7 +296,7 @@ class PPdefinitions(object): 'show-text-y':{'param':'show-text-y','shape':'entry','text':'Show Text y Position','must':'no','read-only':'no'}, 'start-show':{'param':'start-show','shape':'entry','text':'Start Shows','must':'no','read-only':'no'}, 'tab-animation':{'shape':'tab','name':'animation','text':'Animation'}, - 'tab-child':{'shape':'tab','name':'child','text':'Child Show'}, + 'tab-child':{'shape':'tab','name':'child','text':'Child Track'}, 'tab-controls':{'shape':'tab','name':'controls','text':'Controls'}, 'tab-eggtimer':{'shape':'tab','name':'eggtimer','text':'Egg Timer'}, 'tab-links':{'shape':'tab','name':'links','text':'Links'}, @@ -399,39 +401,38 @@ class PPdefinitions(object): 'show-control-begin','show-control-end', 'tab-animate','sep', 'animate-begin','animate-clear','animate-end' - ], + ] - 'menu-background':[ - 'tab-track','sep', - 'type','title','track-ref','location' - ] + } new_tracks={ - 'video':{'title':'New Video','track-ref':'','type':'video','location':'','thumbnail':'','freeze-at-end':'yes','seamless-loop':'no','omx-audio':'','omx-volume':'','omx-window':'','omx-other-options': '','background-colour':'','background-image':'','display-show-background':'yes','display-show-text':'yes','track-text':'','track-text-font':'', - 'track-text-colour':'','track-text-x':'0','track-text-y':'0','links':'','show-control-begin':'','show-control-end':'','animate-begin':'','animate-clear':'no','animate-end':'','plugin':''}, + 'video':{'title':'New Video','track-ref':'','type':'video','location':'','thumbnail':'','freeze-at-end':'yes','seamless-loop':'no', + 'omx-audio':'','omx-volume':'','omx-window':'','omx-other-options': '','background-colour':'','background-image':'','display-show-background':'yes','display-show-text':'yes', + 'track-text':'','track-text-font':'Helvetica 20 bold', + 'track-text-colour':'white','track-text-x':'0','track-text-y':'40','links':'','show-control-begin':'','show-control-end':'','animate-begin':'','animate-clear':'no','animate-end':'','plugin':''}, 'message':{'title':'New Message','track-ref':'','type':'message','text':'','thumbnail':'','duration':'','message-font':'Helvetica 30 bold','message-colour':'white','message-justify':'left','message-x':'','message-y':'', - 'track-text':'','track-text-font':'','track-text-colour':'','track-text-x':'0','track-text-y':'0', - 'background-colour':'','background-image':'','display-show-background':'yes','display-show-text':'no','links':'','show-control-begin':'','show-control-end':'','animate-begin':'','animate-clear':'no','animate-end':'','plugin':''}, + 'track-text':'','track-text-font':'Helvetica 20 bold','track-text-colour':'white','track-text-x':'0','track-text-y':'40', + 'background-colour':'','background-image':'','display-show-background':'yes','display-show-text':'yes','links':'','show-control-begin':'','show-control-end':'','animate-begin':'','animate-clear':'no','animate-end':'','plugin':''}, 'show':{'title':'New Show','track-ref':'','type':'show','sub-show':'','thumbnail':''}, - 'image':{'title':'New Image','track-ref':'','type':'image','location':'','thumbnail':'','duration':'','transition':'','image-window':'','background-colour':'','background-image':'','display-show-background':'yes','display-show-text':'yes','track-text':'','track-text-font':'', - 'track-text-colour':'','track-text-x':'0','track-text-y':'0','links':'','show-control-begin':'','show-control-end':'','animate-begin':'','animate-clear':'no','animate-end':'','plugin':''}, + 'image':{'title':'New Image','track-ref':'','type':'image','location':'','thumbnail':'','duration':'','transition':'','image-window':'', + 'background-colour':'','background-image':'','display-show-background':'yes','display-show-text':'yes','track-text':'','track-text-font':'Helvetica 20 bold', + 'track-text-colour':'white','track-text-x':'0','track-text-y':'40','links':'','show-control-begin':'','show-control-end':'','animate-begin':'','animate-clear':'no','animate-end':'','plugin':''}, 'audio':{'title':'New Audio','track-ref':'','type':'audio','location':'', 'thumbnail':'','duration':'','audio-speaker':'','mplayer-audio':'','mplayer-volume':'','display-show-text':'yes', - 'mplayer-other-options':'','clear-screen':'no','background-colour':'','background-image':'','display-show-background':'yes','track-text':'','track-text-font':'','track-text-colour':'','track-text-x':'0','track-text-y':'0','links':'','show-control-begin':'','show-control-end':'','animate-begin':'','animate-clear':'no','animate-end':'','plugin':''}, + 'mplayer-other-options':'','clear-screen':'no', + 'background-colour':'','background-image':'','display-show-background':'yes', + 'track-text':'','track-text-font':'Helvetica 20 bold','track-text-colour':'white','track-text-x':'0','track-text-y':'40','links':'','show-control-begin':'','show-control-end':'','animate-begin':'','animate-clear':'no','animate-end':'','plugin':''}, 'web':{'title':'New Web','track-ref':'','type':'web','location':'', 'thumbnail':'','duration':'','web-window':'','display-show-text':'yes', - 'background-colour':'','background-image':'','display-show-background':'yes','track-text':'','track-text-font':'','track-text-colour':'','track-text-x':'0','track-text-y':'0','links':'','show-control-begin':'','show-control-end':'','animate-begin':'','animate-clear':'no','animate-end':'','browser-commands':'','plugin':''}, - - - 'menu-background':{'title':'New Menu Background','track-ref':'pp-menu-background','type':'menu-background','location':''}, - - 'child-show': {'title':'New Child Show','track-ref':'pp-child-show','type':'show','sub-show':'','thumbnail':''} + 'background-colour':'','background-image':'','display-show-background':'yes', + 'track-text':'','track-text-font':'Helvetica 20 bold','track-text-colour':'white','track-text-x':'0','track-text-y':'40','links':'', + 'show-control-begin':'','show-control-end':'','animate-begin':'','animate-clear':'no','animate-end':'','browser-commands':'','plugin':''} } diff --git a/pp_editor.py b/pp_editor.py index 2ab19f3..a9a28c5 100644 --- a/pp_editor.py +++ b/pp_editor.py @@ -516,7 +516,7 @@ def edit_show(self,show_types,field_specs): def open_medialists(self,profile_dir): self.medialists = [] for this_file in os.listdir(profile_dir): - if this_file.endswith(".json") and this_file != 'pp_showlist.json': + if this_file.endswith(".json") and this_file not in ('pp_showlist.json','pp_schedule.json'): self.medialists = self.medialists + [this_file] self.medialists_display.delete(0,self.medialists_display.size()) for index in range (len(self.medialists)): @@ -790,7 +790,7 @@ def update_profile(self): # UPDATE MEDIALISTS AND THEIR TRACKS for this_file in os.listdir(self.pp_profile_dir): - if this_file.endswith(".json") and this_file != 'pp_showlist.json': + if this_file.endswith(".json") and this_file not in ('pp_showlist.json','pp_schedule.json'): self.mon.log (self,"Updating medialist " + this_file) # open a medialist and update its tracks ifile = open(self.pp_profile_dir + os.sep + this_file, 'rb') diff --git a/pp_gapshow.py b/pp_gapshow.py index 5d63815..0758842 100644 --- a/pp_gapshow.py +++ b/pp_gapshow.py @@ -69,18 +69,7 @@ def play(self,end_callback,show_ready_callback, direction_command,level,controls # get the previous shower and player from calling show # Show.base_get_previous_player_from_parent(self) - # get control bindings for this show - controlsmanager=ControlsManager() - if self.show_params['disable-controls'] == 'yes': - self.controls_list=[] - else: - reason,message,self.controls_list= controlsmanager.get_controls(self.show_params['controls']) - if reason=='error': - self.mon.err(self,message) - self.end('error',"error in controls") - return - # print 'controls',reason,self.show_params['controls'],self.controls_list # unpack end trigger if self.show_params['trigger-end-type'] == 'duration': @@ -185,12 +174,19 @@ def do_operation(self,operation,edge,source): elif operation == 'pause': if self.current_player is not None: self.current_player.input_pressed('pause') + + elif operation in ('no-command','null'): + return # if the operation is omxplayer mplayer or uzbl 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.current_player is not None: self.current_player.input_pressed(operation) + else: + self.mon.err(self,"unknown operation: "+ operation) + self.end('error',"unknown operation") + def next(self): @@ -299,9 +295,9 @@ def start_show(self): self.start_load_show_loop(self.medialist.selected_track()) else: if self.medialist.start() is False: - # list is empty - display a message for 10 secs and then retry + # list is empty - display a message for 5 secs and then retry Show.display_admin_message(self,Show.base_resource(self,'mediashow','m11')) - self.canvas.after(10000,self.remove_list_empty_message) + self.canvas.after(5000,self.remove_list_empty_message) else: self.start_load_show_loop(self.medialist.selected_track()) @@ -328,6 +324,20 @@ def start_load_show_loop(self,selected_track): else: self.enable_child=False + # get control bindings for this show + # needs to be done for each track as track can override the show controls + self.controlsmanager=ControlsManager() + 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 + + # print 'controls',reason,self.show_params['controls'],self.controls_list + # load the track or show # params - track,enable_menu Show.base_load_track_or_show(self,selected_track,self.what_next_after_load,self.end_shower,self.enable_child) @@ -393,7 +403,7 @@ def pretty_what_next_after_showing_state(self): state += '\n* previous track signal ' + str(self.previous_track_signal) state += '\n* next track signal ' + str(self.next_track_signal) state += '\n* req_next ' + self.req_next - state += '\n * direction ?old ' + self.direction + state += '\n * direction ' + self.direction return state +'\n' @@ -435,7 +445,7 @@ def what_next_after_showing(self): if self.interval_timer_signal is True: self.interval_timer_signal=False self.waiting_for_interval=False - # print 'RECEIVED INTERAL TIMER SIGNAL - STARTING SHOW' + # print 'RECEIVED INTERNAL TIMER SIGNAL - STARTING SHOW' self.wait_for_trigger() else: self.poll_for_interval_timer=self.canvas.after(1000,self.what_next_after_showing) @@ -448,15 +458,13 @@ def what_next_after_showing(self): self.ending_reason='user-stop' Show.base_close_or_unload(self) -## # has content of list been changed (replaced if it has, used for content of livelist) -## elif self.medialist.replace_if_changed() is True: -## self.ending_reason='change-medialist' -## self.wait_for_trigger() -## -## elif self.medialist.length() == 0: -## self.load_first_track() + # has content of list been changed (replaced if it has, used for content of livelist) + elif self.medialist.replace_if_changed() is True: + self.ending_reason='change-medialist' + Show.base_close_or_unload(self) + - # otherwise show the next track + # otherwise consider operation that might show the next track else: # setup default direction for if statement self.direction='forward' @@ -489,7 +497,9 @@ def what_next_after_showing(self): self.start_load_show_loop(child_track) else: self.mon.err(self,"Child not found in medialist: "+ self.child_track_ref) - self.end('error',"child not found in medialist") + self.ending_reason='error' + Show.base_close_or_unload(self) + # self.end('error',"child not found in medialist") # skip to next track on user input or after subshow elif self.next_track_signal is True or self.req_next == 'do-next': @@ -500,14 +510,22 @@ def what_next_after_showing(self): if self.show_params['sequence'] == "ordered" and self.show_params['repeat'] == 'repeat': # self.state='waiting' self.wait_for_trigger() - elif self.show_params['sequence'] == "ordered" and self.show_params['repeat'] == 'single-run' and self.level != 0: - self.end('do-next',"Return from Sub Show") + elif self.show_params['sequence'] == "ordered" and self.show_params['repeat'] == 'single-run': + if self.level != 0: + self.direction='forward' + self.end('do-next',"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: - # repeat if at top level even if single run + # shuffling - just do next track + self.direction='forward' self.medialist.next(self.show_params['sequence']) - self.start_load_show_loop(self.medialist.selected_track()) + self.start_load_show_loop(self.medialist.selected_track()) else: - # print 'not at end' + # not at end just do next track self.medialist.next(self.show_params['sequence']) self.start_load_show_loop(self.medialist.selected_track()) @@ -522,15 +540,22 @@ def what_next_after_showing(self): # self.state='waiting' self.direction='backward' self.wait_for_trigger() - elif self.show_params['sequence'] == "ordered" and self.show_params['repeat'] == 'single-run' and self.level != 0: - self.direction='backward' - self.end('do-previous',"Return from Sub Show") + elif self.show_params['sequence'] == "ordered" and self.show_params['repeat'] == 'single-run': + if self.level != 0: + self.direction='backward' + self.end('do-previous',"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: - # repeat if at top level even if single run + # shuffling - just do previous track self.direction='backward' 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()) @@ -558,7 +583,15 @@ def what_next_after_showing(self): # single run elif self.show_params['sequence'] == "ordered" and self.show_params['repeat'] == 'single-run': - self.end('normal',"End of 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) + # shuffling so there is no end condition, get out of end test elif self.show_params['sequence'] == "shuffle": @@ -587,6 +620,19 @@ def what_next_after_showing(self): # 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) + + + # 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) diff --git a/pp_hyperlinkshow.py b/pp_hyperlinkshow.py index e92b50e..d33beb6 100644 --- a/pp_hyperlinkshow.py +++ b/pp_hyperlinkshow.py @@ -205,8 +205,8 @@ def input_pressed_this_show(self,symbol,edge,source): elif link_op == 'exit': self.exit() - elif link_op=='no-command': - pass + elif link_op in ('no-command','null'): + return # in-track operations elif link_op =='pause': @@ -334,7 +334,7 @@ def start_load_show_loop(self,selected_track): # read the show links. Track links will be added by track_ready_callback - # needs to be done in loop as each track adds different links o the show links + # needs to be done in loop as each track adds different links to the show links links_text=self.show_params['links'] reason,message,self.links=self.path.parse_links(links_text,self.allowed_links) if reason == 'error': @@ -582,8 +582,9 @@ def track_ready_callback(self,enable_show_background): links_text=self.current_player.get_links() reason,message,track_links=self.path.parse_links(links_text,self.allowed_links) if reason == 'error': - self.mon.err(self,message + " in page") - self.end('error',message) + 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 diff --git a/pp_livelist.py b/pp_livelist.py index bf185e8..0cf6fd6 100644 --- a/pp_livelist.py +++ b/pp_livelist.py @@ -22,19 +22,17 @@ def open_list(self,filename,showlist_issue): """ opens a saved medialist medialists are stored as json arrays. - In medialists for liveshows igore any anymous tracks so length=0 + In liveshows tracks from the medialist are stored speretaley from live tracks. Only track with track references are used """ ifile = open(filename, 'rb') mdict = json.load(ifile) ifile.close() - self._tracks = mdict['tracks'] + self.medialist_tracks = mdict['tracks'] if 'issue' in mdict: self.issue= mdict['issue'] else: self.issue="1.0" if self.issue==showlist_issue: - self._num_tracks=0 - self._selected_track_index=-1 return True else: return False @@ -62,8 +60,22 @@ def next(self,sequence): # print self._selected_track_index self.select(self._selected_track_index) return True - + + def previous(self,sequence): + if sequence=='ordered': + if self._selected_track_index == 0: + self._selected_track_index=self._num_tracks-1 + else: + self._selected_track_index -=1 + self.select(self._selected_track_index) + return True + else: + self._selected_track_index=random.randint(0,self._num_tracks-1) + # print self._selected_track_index + self.select(self._selected_track_index) + return True + def start(self): self.new_livelist_create() self._tracks = copy.deepcopy(self.new_livelist) @@ -111,9 +123,19 @@ def selected_track(self): """returns a dictionary containing all fields in the selected track """ return self._selected_track - # lookup index from show_ref, return -1 because liveshow does not have child tracks. - def index_of_track(self,show_ref): + + # lookup index from show_ref + def index_of_track(self,wanted_track): + index = 0 + for track in self.medialist_tracks: + if track['track-ref']==wanted_track: + return index + index +=1 return -1 + + # in livelist medialist tracks and live tracks are stored seperately + def track(self,index): + return self.medialist_tracks[index] def replace_if_changed(self): self.new_livelist_create() diff --git a/pp_liveshow.py b/pp_liveshow.py index 1e7c304..1a6e6d1 100644 --- a/pp_liveshow.py +++ b/pp_liveshow.py @@ -31,6 +31,11 @@ def __init__(self, self.options=command_options() + + def play(self,end_callback,show_ready_callback, direction_command,level,controls_list): + + self.end_callback=end_callback + # get the livetracks directories if self.show_params['live-tracks-dir1'] != '': self.pp_live_dir1= self.show_params['live-tracks-dir1'] @@ -76,12 +81,10 @@ def __init__(self, self.mon.err(self,"Failed to find live tracks Directory 2"+ self.pp_live_dir2) self.end('error','Failed to find live tracks dir 2') - - - # use the appropriate medialist - self.medialist=LiveList(show_params['sequence']) + self.medialist=LiveList(self.show_params['sequence']) # and pass directories to livelist self.medialist.live_tracks(self.pp_live_dir1,self.pp_live_dir2) + GapShow.play(self,end_callback,show_ready_callback, direction_command,level,controls_list) diff --git a/pp_medialist.py b/pp_medialist.py index 6b6d5d0..f595cd6 100644 --- a/pp_medialist.py +++ b/pp_medialist.py @@ -82,7 +82,7 @@ def selected_track_index(self): def track(self,index): return self._tracks[index] - + def selected_track(self): """returns a dictionary containing all fields in the selected track """ return self._selected_track diff --git a/pp_mediashow.py b/pp_mediashow.py index aa85416..01a0b56 100644 --- a/pp_mediashow.py +++ b/pp_mediashow.py @@ -28,8 +28,15 @@ def __init__(self, command_callback) + def play(self,end_callback,show_ready_callback, direction_command,level,controls_list): + # use the appropriate medialist - self.medialist=MediaList(show_params['sequence']) + self.medialist=MediaList(self.show_params['sequence']) + + GapShow.play(self,end_callback,show_ready_callback, direction_command,level,controls_list) + + + diff --git a/pp_menuplayer.py b/pp_menuplayer.py index 4f72aaf..b148e0a 100644 --- a/pp_menuplayer.py +++ b/pp_menuplayer.py @@ -640,7 +640,7 @@ def display_icon_image(self): standard=self.pp_dir+os.sep+'pp_home'+os.sep+'pp_resources'+os.sep+icon_type+'.png' if os.path.exists(standard) is True: self.pil_image=Image.open(standard) - self.mon.log(self,'WARNING: default thumbnail used for '+self.medialist.selected_track()['title']) + self.mon.warn(self,'Default thumbnail used for '+self.medialist.selected_track()['title']) else: self.pil_image=None diff --git a/pp_menushow.py b/pp_menushow.py index 177149d..553a2dc 100644 --- a/pp_menushow.py +++ b/pp_menushow.py @@ -367,7 +367,8 @@ def what_next_after_showing(self): # and if necessary close it def track_ready_callback(self,enable_show_background): self.delete_eggtimer() - self.sr.enable_click_areas(self.controls_list) + if self.menu_showing is True: + 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 diff --git a/pp_pathmanager.py b/pp_pathmanager.py index 3b2dd6e..df7d695 100644 --- a/pp_pathmanager.py +++ b/pp_pathmanager.py @@ -7,7 +7,7 @@ def __init__(self): self.debug=False # remove the # to enable display of the path operations and stack in the terminal window. - self.debug=False + # self.debug=True self.path_stack=[] @@ -111,7 +111,7 @@ def parse_link(self,line,allowed_list): arg='' return '',[symbol,operation,arg] else: - return "unknown operation",['','',''] + return "unknown operation: "+operation,['','',''] def merge_links(self,current_links,track_links): diff --git a/pp_player.py b/pp_player.py index 220e32d..35c5b6d 100644 --- a/pp_player.py +++ b/pp_player.py @@ -145,7 +145,7 @@ def show_control(self,show_control_text): for line in lines: if line.strip() == "": continue - # print 'show control command: ',linr + # print 'show control command: ',line self.command_callback(line) diff --git a/pp_radiobuttonshow.py b/pp_radiobuttonshow.py index 610f4ef..e0462c7 100644 --- a/pp_radiobuttonshow.py +++ b/pp_radiobuttonshow.py @@ -64,7 +64,7 @@ def __init__(self, # create an instance of PathManager - only used to parse the links. self.path = PathManager() - self.allowed_links=('play','pause','exit','return') + self.allowed_links=('play','pause','exit','return','null','no-command') # init variables self.track_timeout_timer=None self.show_timeout_timer=None @@ -87,12 +87,7 @@ def play(self,end_callback,show_ready_callback,direction_command,level,controls_ self.mon.trace(self,self.show_params['show-ref']) - # read the show links. Track links will NOT be added by ready_callback - links_text=self.show_params['links'] - reason,message,self.links=self.path.parse_links(links_text,self.allowed_links) - if reason == 'error': - self.mon.err(self,message + " in show") - self.end('error',message) + # get the previous player and show from calling show # Show.base_get_previous_player_from_parent(self) @@ -149,6 +144,9 @@ def input_pressed_this_show(self,symbol,edge,source): 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: @@ -233,6 +231,14 @@ def start_load_show_loop(self,selected_track): # start timeout for the track if required if self.current_track_ref != self.first_track_ref and int(self.show_params['track-timeout']) != 0: self.track_timeout_timer=self.canvas.after(int(self.show_params['track-timeout'])*1000,self.track_timeout_callback) + + # 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 + links_text=self.show_params['links'] + reason,message,self.links=self.path.parse_links(links_text,self.allowed_links) + if reason == 'error': + self.mon.err(self,message + " in show") + self.end('error',message) # load the track or show # params - track,, track loaded callback, end eshoer callback,enable_menu @@ -349,6 +355,16 @@ def what_next_after_showing(self): # and if necessary close it def track_ready_callback(self,enable_show_background): self.delete_eggtimer() + + #merge links from the track + links_text=self.current_player.get_links() + reason,message,track_links=self.path.parse_links(links_text,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) diff --git a/pp_screendriver.py b/pp_screendriver.py index 7e20863..556ab11 100644 --- a/pp_screendriver.py +++ b/pp_screendriver.py @@ -1,18 +1,25 @@ import os import ConfigParser +import copy +from Tkinter import NW +from PIL import Image +from PIL import ImageTk from pp_utils import Monitor class ScreenDriver(object): config=None canvas = None ##The Pi presents canvas, click areas are draawn on this, not individual show canvases. - + image_obj=[] + def __init__(self): self.mon=Monitor() # read screen.cfg def read(self,pp_dir,pp_home,pp_profile): + self.pp_dir=pp_dir + self.pp_home=pp_home if ScreenDriver.config is None: # try inside profile tryfile=pp_profile+os.sep+"screen.cfg" @@ -39,7 +46,6 @@ def read(self,pp_dir,pp_home,pp_profile): self.mon.log(self,"screen.cfg read from "+ filename) return 'normal','screen.cfg read' - def click_areas(self): return ScreenDriver.config.sections() @@ -53,6 +59,7 @@ def make_click_areas(self,canvas,callback): ScreenDriver.canvas=canvas self.callback=callback reason='' + ScreenDriver.image_obj=[] for area in self.click_areas(): reason,message,points = self.parse_points(self.get(area,'points'),self.get(area,'name')) if reason == 'error': @@ -62,6 +69,34 @@ def make_click_areas(self,canvas,callback): outline=self.get (area,'outline-colour'), tags=("pp-click-area",self.get(area,'name')), state='hidden') + + # image for the button + image_name=self.get(area,'image') + if image_name !='': + image_width = int(self.get(area,'image-width')) + image_height = int(self.get(area,'image-height')) + image_path=self.complete_path(image_name) + # image_path=image_name + if os.path.exists(image_path) is False: + self.pil_image=None + reason='error' + message = 'image not found: '+ image_path + break + else: + self.pil_image=Image.open(image_path) + if self.pil_image is not None: + self.pil_image=self.pil_image.resize((image_width,image_height)) + self.photo_image_id=ImageTk.PhotoImage(self.pil_image) + image_id=self.canvas.create_image(points[0],points[1], + image=self.photo_image_id, + anchor=NW, + tags=('pp-click-area',self.get(area,'name')), + state='hidden') + del self.pil_image + ScreenDriver.image_obj.append(self.photo_image_id) + + + # write the label at the centroid if self.get(area,'text') != '': vertices = len(points)/2 @@ -83,6 +118,7 @@ def make_click_areas(self,canvas,callback): tags=('pp-click-area',self.get(area,'name')), state='hidden') ScreenDriver.canvas.bind('',self.click_pressed) + if reason == 'error': return 'error',message @@ -120,7 +156,7 @@ def hide_click_areas(self,links): # hide click areas for link in links: if self.is_click_area(link[0]) and link[1] != 'null': - print 'disabling link ',link[0] + # print 'disabling link ',link[0] ScreenDriver.canvas.itemconfig(link[0],state='hidden') # this does not seem to change the colour of the polygon @@ -141,3 +177,9 @@ def parse_points(self,points_text,area): return 'error','point is not a positive integer in click area: '+area,[] return 'normal','parsed points OK',points + + 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 diff --git a/pp_show.py b/pp_show.py index ac5880a..4b2bb8f 100644 --- a/pp_show.py +++ b/pp_show.py @@ -82,6 +82,7 @@ def base__init__(self, self.ending_reason='' self.background_obj=None self.background_file='' + self.level=0 # get background image from profile. if self.show_params['background-image'] != '': @@ -187,7 +188,7 @@ def base_load_track_or_show(self,selected_track,loaded_callback,end_shower_callb loaded_callback, enable_menu=enable_menu) - # dummy, must be overidden by derived class + # DUMMY, must be overidden by derived class def what_next_after_showing(self): self.mon.err(self,"what_next_after showing not overidden") # set what to do when closed or unloaded @@ -313,7 +314,7 @@ def base_close_or_unload(self): self.end('normal',"show quit by stop operation") else: - self.mon.err(self,"Unhandled ending_reason: ") + self.mon.fatal(self,"Unhandled ending_reason: ") self.end('error',"Unhandled ending_reason") @@ -345,6 +346,12 @@ def _wait_for_end(self): self.current_player.hide() self.current_player=None self.base_close_previous() + + elif self.ending_reason == 'change-medialist': + self.current_player.hide() + self.current_player=None + # self.base_close_previous() + self.wait_for_trigger() elif self.ending_reason == 'show-timeout': self.current_player.hide() @@ -352,10 +359,16 @@ def _wait_for_end(self): self.end('normal',"show timeout") elif self.ending_reason == 'user-stop': - self.end('normal',"show quit by stop operation") + if self.level !=0: + self.end('normal',"show quit by stop operation") + else: + self.current_player.hide() + self.current_player=None + self.base_close_previous() + else: - self.mon.err(self,"Unhandled ending_reason: ") + self.mon.fatal(self,"Unhandled ending_reason: " + self.ending_reason) self.end('error',"Unhandled ending_reason") else: self.mon.trace(self,' - current is None ' + self.mon.pretty_inst(self.current_player) + ' ' + self.ending_reason) diff --git a/pp_showmanager.py b/pp_showmanager.py index 4744395..6bdbaf6 100644 --- a/pp_showmanager.py +++ b/pp_showmanager.py @@ -4,13 +4,13 @@ class ShowManager(object): """ - ShowManager manages PiPresents' concurrent shows. It does not manage sub-shows or child-shows. - concurrent shows are always top level (level 0 shows: - They can be started by the start show or by 'myshow start' in the Show Control field of players - They can be exiteded either by 'myshow exit' in the Show Control field in players + ShowManager manages PiPresents' concurrent shows. It does not manage sub-shows or child-shows but has a bit of common code to initilise them + concurrent shows are always top level (level 0) shows: + They can be opened/closed by the start show(open only) or by 'open/close myshow' in the Show Control field of players, by time of day sceduler or by OSC + + Two shows with the same show reference cannot be run concurrently as there is no way to reference an individual instance. + However a workaround is to make the secong instance a subshow of a mediashow with a different reference. - a show with the same reference should not be run twice as there is no way to reference an individual instance when exiting - ??? this could be changed as there is single-run to exit them, the exit command could exit all instances. """ # Declare class variables @@ -20,29 +20,32 @@ class ShowManager(object): SHOW_TEMPLATE=['',None] SHOW_REF= 0 # show-reference - name of the show as in editor SHOW_OBJ = 1 # the python object + showlist=[] - # Initialise, first time through only in pipresents.py + # Initialise class variables, first time through only in pipresents.py - def init(self,canvas,all_shows_ended_callback,command_callback): + def init(self,canvas,all_shows_ended_callback,command_callback,showlist): ShowManager.all_shows_ended_callback=all_shows_ended_callback ShowManager.shows=[] ShowManager.shutdown_required=False ShowManager.canvas=canvas ShowManager.command_callback = command_callback + ShowManager.showlist = showlist # ************************************** # functions to manipulate show register # ************************************** - def pretty_shows(self): - shows='\n' - for show in ShowManager.shows: - shows += show[0] +'\n' - return shows + def register_shows(self): + for show in ShowManager.showlist.shows(): + if show['show-ref'] != 'start': + reason,message=self.register_show(show['show-ref']) + if reason =='error': + return reason,message + return 'normal','shows regiistered' -# adds a new concurrent show to the register if not already there, returns an index for use by start and exit def register_show(self,ref): registered=self.show_registered(ref) @@ -51,14 +54,14 @@ def register_show(self,ref): index=len(ShowManager.shows)-1 ShowManager.shows[index][ShowManager.SHOW_REF]=ref ShowManager.shows[index][ShowManager.SHOW_OBJ]=None - self.mon.trace(self,' - new show: show_ref = ' + ref + ' index = ' + str(index)) - return index + self.mon.trace(self,' - register show: show_ref = ' + ref + ' index = ' + str(index)) + return'normal','show registered' else: - self.mon.warn(self, ' show already registerd: show_ref = ' + ref + ' show_id= ' + registered) - return registered + # self.mon.err(self, ' more than one show in showlist with show-ref: ' + ref ) + return 'error', ' more than one show in showlist with show-ref: ' + ref -# is the show registered? -# can be used to return the index to the show + # is the show registered? + # can be used to return the index to the show def show_registered(self,show_ref): index=0 for show in ShowManager.shows: @@ -67,13 +70,13 @@ def show_registered(self,show_ref): index+=1 return -1 -# needs calling program to check that the show is not already running + # needs calling program to check that the show is not already running def set_running(self,index,show_obj): ShowManager.shows[index][ShowManager.SHOW_OBJ]=show_obj self.mon.trace(self, 'show_ref= ' + ShowManager.shows[index][ShowManager.SHOW_REF] + ' show_id= ' + str(index)) - self.mon.trace(self,'concurrent shows:\n' + self.pretty_shows()) -# is the show running? + + # is the show running? def show_running(self,index): if ShowManager.shows[index][ShowManager.SHOW_OBJ] is not None: return ShowManager.shows[index][ShowManager.SHOW_OBJ] @@ -83,10 +86,10 @@ def show_running(self,index): def set_exited(self,index): ShowManager.shows[index][ShowManager.SHOW_OBJ]=None self.mon.trace(self,'show_ref= ' + ShowManager.shows[index][ShowManager.SHOW_REF] + ' show_id= ' + str(index)) - self.mon.trace(self,'concurrent shows:\n' + self.pretty_shows()) -# are all shows exited? + + # are all shows exited? def all_shows_exited(self): all_exited=True for show in ShowManager.shows: @@ -94,7 +97,13 @@ def all_shows_exited(self): all_exited=False return all_exited - + # fromat for printing + def pretty_shows(self): + shows='\n' + for show in ShowManager.shows: + shows += show[ShowManager.SHOW_REF] +'\n' + return shows + # ********************************* # show control # ********************************* @@ -139,19 +148,18 @@ def exit_show(self,show_ref): def start_show(self,show_ref): - show_index = self.showlist.index_of_show(show_ref) - if show_index <0: + index=self.show_registered(show_ref) + if index <0: return 'error',"Show not found in showlist: "+ show_ref - + show_index = self.showlist.index_of_show(show_ref) show=self.showlist.show(show_index) - index=self.register_show(show_ref) reason,message,show_canvas=self.compute_show_canvas(show) if reason == 'error': return reason,message # print 'STARTING TOP LEVEL SHOW',show_canvas - self.mon.log(self,'Starting Show from: ' + self.show_params['show-ref']+ ' '+ str(self.show_id)+" show_ref:"+ show_ref + ' show_id' + str(index) ) + self.mon.log(self,'Starting Show: ' + show_ref + ' from: ' + self.show_params['show-ref']) if self.show_running(index): - self.mon.log(self,"show already running "+show_ref) + self.mon.warn(self,"show already running so ignoring command: "+show_ref) return 'normal','this concurrent show already running' show_obj = self.init_show(index,show,show_canvas) if show_obj is None: @@ -271,8 +279,8 @@ def compute_show_canvas(self,show_params): canvas['canvas-obj']= ShowManager.canvas status,message,self.show_canvas_x1,self.show_canvas_y1,self.show_canvas_x2,self.show_canvas_y2= self.parse_show_canvas(show_params['show-canvas']) if status == 'error': - self.mon.err(self,'show canvas error: ' + message + ' in ' + show_params['show-canvas']) - return 'error',message,canvas + # self.mon.err(self,'show canvas error: ' + message + ' in ' + show_params['show-canvas']) + return 'error','show canvas error: ' + message + ' in ' + show_params['show-canvas'],canvas else: self.show_canvas_width = self.show_canvas_x2 - self.show_canvas_x1 self.show_canvas_height=self.show_canvas_y2 - self.show_canvas_y1 diff --git a/pp_utils.py b/pp_utils.py index 056efa1..b29b82b 100644 --- a/pp_utils.py +++ b/pp_utils.py @@ -73,14 +73,17 @@ def set_log_level(self,level): # PRINTING def newline(self,num): - for i in range(0,num): - print + if Monitor.log_level & ~ (Monitor.m_warn|Monitor.m_err|Monitor.m_fatal) != 0: + for i in range(0,num): + print def fatal(self,caller,text): r_class=caller.__class__.__name__ + r_func = sys._getframe(1).f_code.co_name + r_line = str(sys._getframe(1).f_lineno) if self.enabled(r_class,Monitor.m_fatal) is True: - print "%.2f" % (time.time()-Monitor.start_time), " FATAL: ",r_class+": ", text - Monitor.ofile.write (" SYSTEM ERROR: " + r_class + ": " + text + "\n") + print "%.2f" % (time.time()-Monitor.start_time), " FATAL: ",r_class+"/"+ r_func + "/"+ r_line + ": ", text + Monitor.ofile.write (" SYSTEM ERROR: " + r_class +"/"+ r_func + "/"+ r_line + ": " + text + "\n") tkMessageBox.showwarning(r_class ,text) def err(self,caller,text): diff --git a/pp_validate.py b/pp_validate.py index 9f022ab..85a16f8 100644 --- a/pp_validate.py +++ b/pp_validate.py @@ -57,7 +57,7 @@ def validate_profile(self, root, pp_dir, pp_home, pp_profile,editor_issue,displa self.result.display('t', "Validation Aborted") return False - if medialist_file.endswith(".json") and medialist_file != 'pp_showlist.json': + if medialist_file.endswith(".json") and medialist_file not in ('pp_showlist.json','pp_schedule.json'): self.result.display('t',"\nChecking medialist '"+medialist_file+"'") v_media_lists.append(medialist_file) @@ -87,6 +87,8 @@ def validate_profile(self, root, pp_dir, pp_home, pp_profile,editor_issue,displa if track['track-ref'] == '': anonymous+=1 else: + if track['track-ref'] in v_track_labels: + self.result.display('f',"'duplicate track reference: "+ track['track-ref']) v_track_labels.append(track['track-ref']) # warn if media tracks blank where optional @@ -222,7 +224,7 @@ def validate_profile(self, root, pp_dir, pp_home, pp_profile,editor_issue,displa # make a list of the track labels v_track_labels=[] for track in tracks: - if track['track-ref'] in ('pp-menu-background','pp-child-show'): + if track['track-ref'] !='': v_track_labels.append(track['track-ref']) @@ -247,7 +249,7 @@ def validate_profile(self, root, pp_dir, pp_home, pp_profile,editor_issue,displa if show['type'] in ("mediashow",'liveshow'): if show['child-track-ref'] != '': if show['child-track-ref'] not in v_track_labels: - self.result.display('f',"'child track ' is not in medialist: " + show['child-track-ref']) + self.result.display('f',"'Child Track ' " + show['child-track-ref'] + ' is not in medialist' ) if not show['hint-y'].isdigit(): self.result.display('f',"'hint-y' is not 0 or a positive integer") if not show['hint-x'].isdigit(): self.result.display('f',"'hint-x' is not 0 or a positive integer")