Ejemplo n.º 1
0
    def __init__(self,show_id,root,canvas,show_params,track_params,pp_dir,pp_home,pp_profile):

        self.mon=Monitor()
        self.mon.on()

        self.root=root
        self.canvas=canvas
        self.show_id=show_id
        self.track_params=track_params
        self.show_params=show_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile

       # get config from medialist if there.
       
        if 'duration' in self.track_params and self.track_params['duration']<>"":
            self.duration= int(self.track_params['duration'])
        else:
            self.duration= int(self.show_params['duration'])

        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>"":
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']
            
        self.centre_x = int(self.canvas['width'])/2
        self.centre_y = int(self.canvas['height'])/2
        
        # keep tick as an integer sub-multiple of 1 second         
        self.tick = 100 # tick time for image display (milliseconds)
        self.dwell = 1000*self.duration

        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO() 
Ejemplo n.º 2
0
    def __init__(self, track, pp_dir, pp_home, show_params, ct):

        self.track = track
        self.show_params = show_params
        self.pp_home = pp_home
        self.ct = ct
        self.break_from_loop = False

        # create and instance of a Tkinter top level window and refer to it as 'my_window'
        my_window = Tk()
        my_window.title("AudioPlayer Test Harness")

        # change the look of the window
        my_window.configure(background='grey')
        window_width = 1500
        window_height = 900

        canvas_height = window_height
        canvas_width = window_width

        #defne response to main window closing
        my_window.protocol("WM_DELETE_WINDOW", self.terminate)

        my_window.geometry("%dx%d+200+20" % (window_width, window_height))

        # Always use CTRL-Break key to close the program as a get out of jail
        my_window.bind("<Break>", self.e_terminate)

        my_window.bind("s", self.play_event)
        my_window.bind("p", self.pause_event)
        my_window.bind("q", self.stop_event)
        my_window.bind("l", self.loop_event)
        my_window.bind("n", self.next_event)

        #setup a canvas onto which will not be drawn the video!!
        canvas = Canvas(my_window, bg='black')
        canvas.config(height=canvas_height, width=canvas_width)
        canvas.pack()
        # make sure focus is set on canvas.
        canvas.focus_set()
        self.canvas = canvas

        #create an instance of PPIO and start polling
        self.ppio = PPIO()
        self.ppio.init(pp_dir, pp_home, self.canvas, 50, self.callback)
        self.ppio.poll()

        my_window.mainloop()
Ejemplo n.º 3
0
    def __init__(self,track,pp_dir,pp_home,show_params,ct):
        
        self.track=track
        self.show_params=show_params
        self.pp_home=pp_home
        self.ct = ct
        self.break_from_loop=False


        # create and instance of a Tkinter top level window and refer to it as 'my_window'
        my_window=Tk()
        my_window.title("AudioPlayer Test Harness")
    
        # change the look of the window
        my_window.configure(background='grey')
        window_width=1500
        window_height=900
    
        canvas_height=window_height
        canvas_width=window_width
    

        #defne response to main window closing
        my_window.protocol ("WM_DELETE_WINDOW", self.terminate)
        
        my_window.geometry("%dx%d+200+20" %(window_width,window_height))

        # Always use CTRL-Break key to close the program as a get out of jail
        my_window.bind("<Break>",self.e_terminate)
    
        my_window.bind("s", self.play_event)
        my_window.bind("p", self.pause_event)
        my_window.bind("q", self.stop_event)
        my_window.bind("l", self.loop_event)
        my_window.bind("n", self.next_event)
        
        #setup a canvas onto which will not be drawn the video!!
        canvas = Canvas(my_window, bg='black')
        canvas.config(height=canvas_height, width=canvas_width)
        canvas.pack()
        # make sure focus is set on canvas.
        canvas.focus_set()
        self.canvas=canvas

        #create an instance of PPIO and start polling
        self.ppio= PPIO()	
        self.ppio.init(pp_dir,pp_home,self.canvas,50,self.callback)
        self.ppio.poll()
         
        my_window.mainloop()
Ejemplo n.º 4
0
    def __init__(self,
                         show_id,
                        canvas,
                        pp_home,
                        show_params,
                        track_params ):
        """       
            canvas - the canvas onto which the video is to be drawn (not!!)
            cd - configuration dictionary
            track_params - config dictionary for this track overides cd
        """
        self.mon=Monitor()
        self.mon.on()
        
        #instantiate arguments
        self.show_id=show_id
        self.show_params=show_params       #configuration dictionary for the videoplayer
        self.pp_home=pp_home
        self.canvas = canvas  #canvas onto which video should be played but isn't! Use as widget for alarm
        self.track_params=track_params
        
        # get config from medialist if there.
        if self.track_params['omx-audio']<>"":
            self.omx_audio= self.track_params['omx-audio']
        else:
            self.omx_audio= self.show_params['omx-audio']
        if self.omx_audio<>"": self.omx_audio= "-o "+ self.omx_audio
        
        if self.track_params['omx-volume']<>"":
            self.omx_volume= self.track_params['omx-volume']
        else:
            self.omx_volume= self.show_params['omx-volume']
        if self.omx_volume<>"": self.omx_volume= "--vol "+ str(int(self.omx_volume)*100) + ' '
        self.omx_volume=' '
        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()        
        
        # could put instance generation in play, not sure which is better.
        self.omx=OMXDriver(self.canvas)
        self._tick_timer=None
        self.error=False
        self.terminate_required=False
        self._init_play_state_machine()
Ejemplo n.º 5
0
    def __init__(self,show_id,canvas,pp_home,show_params,track_params):
        """
                canvas - the canvas onto which the image is to be drawn
                cd -  dictionary of show parameters
                track_params - disctionary of track paramters
        """

        self.mon=Monitor()
        self.mon.on()

        self.show_id=show_id
        self.canvas=canvas
        self.pp_home=pp_home
        self.show_params=show_params
        self.track_params=track_params

        # open resources
        self.rr=ResourceReader()

            
        # get config from medialist if there.
        
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']
        
        if 'duration' in self.track_params and self.track_params['duration']<>"":
            self.duration= int(self.track_params['duration'])
        else:
            self.duration= int(self.show_params['duration'])
            
        if 'transition' in self.track_params and self.track_params['transition']<>"":
            self.transition= self.track_params['transition']
        else:
            self.transition= self.show_params['transition']
  
        # keep dwell and porch as an integer multiple of tick          
        self.porch = 1000 #length of pre and post porches for an image (milliseconds)
        self.tick = 100 # tick time for image display (milliseconds)
        self.dwell = (1000*self.duration)- (2*self.porch)
        if self.dwell<0: self.dwell=0

        self.centre_x = int(self.canvas['width'])/2
        self.centre_y = int(self.canvas['height'])/2

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()
class VideoPlayer:
    """ plays a track using omxplayer
        See pp_imageplayer for common software design description
    """

    _CLOSED = "omx_closed"    #probably will not exist
    _STARTING = "omx_starting"  #track is being prepared
    _PLAYING = "omx_playing"  #track is playing to the screen, may be paused
    _ENDING = "omx_ending"  #track is in the process of ending due to quit or end of track


# ***************************************
# EXTERNAL COMMANDS
# ***************************************

    def __init__(self,
                         show_id,
                         root,
                        canvas,
                        show_params,
                        track_params ,
                         pp_dir,
                        pp_home,
                        pp_profile):

        self.mon=Monitor()
        self.mon.on()
        
        #instantiate arguments
        self.show_id=show_id
        self.root=root
        self.canvas = canvas
        self.show_params=show_params
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile


        # get config from medialist if there.
        if self.track_params['omx-audio']<>"":
            self.omx_audio= self.track_params['omx-audio']
        else:
            self.omx_audio= self.show_params['omx-audio']
        if self.omx_audio<>"": self.omx_audio= "-o "+ self.omx_audio
        
        if self.track_params['omx-volume']<>"":
            self.omx_volume= self.track_params['omx-volume']
        else:
            self.omx_volume= self.show_params['omx-volume']
        if self.omx_volume<>"":
            self.omx_volume= "--vol "+ str(int(self.omx_volume)*100) + ' '

        if self.track_params['omx-window']<>'':
            self.omx_window= self.track_params['omx-window']
        else:
            self.omx_window= self.show_params['omx-window']


        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>"":
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']
        
        self.centre_x = int(self.canvas['width'])/2
        self.centre_y = int(self.canvas['height'])/2
        
        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()        
        
        # could put instance generation in play, not sure which is better.
        self.omx=OMXDriver(self.canvas)
        self.tick_timer=None
        self.init_play_state_machine()



    def play(self, track,
                     showlist,
                     end_callback,
                     ready_callback,
                     enable_menu=False):
                         
        #instantiate arguments
        self.track=track
        self.showlist=showlist
        self.ready_callback=ready_callback   #callback when ready to play
        self.end_callback=end_callback         # callback when finished
        self.enable_menu = enable_menu
 
        # callback to the calling object to e.g remove egg timer and enable click areas.
        if self.ready_callback<>None:
            self.ready_callback()

        # create an  instance of showmanager so we can control concurrent shows
        self.show_manager=ShowManager(self.show_id,self.showlist,self.show_params,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home)

        #set up video window
        reason,message,comand,has_window,x1,y1,x2,y2= self.parse_window(self.omx_window)
        if reason =='error':
            self.mon.err(self,'omx window error: ' + message + ' in ' + self.omx_window)
            self.end_callback(reason,message)
        else:
            if has_window==True:
                self.omx_window= '--win " '+ str(x1) +  ' ' + str(y1) + ' ' + str(x2) + ' ' + str(y2) + ' " '
            else:
                self.omx_window=''

             # Control other shows at beginning
            reason,message=self.show_manager.show_control(self.track_params['show-control-begin'])
            if reason in ('error','killed'):
                self.end_callback(reason,message)
                self=None
            else:      
                #display content
                reason,message=self.display_content()
                if reason == 'error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                    # create animation events
                    reason,message=self.ppio.animate(self.animate_begin_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        # start playing the video.
                        if self.play_state == VideoPlayer._CLOSED:
                            self.mon.log(self,">play track received")
                            self.start_play_state_machine(self.track)
                        else:
                            self.mon.err(self,'play track rejected')
                            self.end_callback('error','play track rejected')
                            self=None

    def terminate(self,reason):
        # circumvents state machine and does not wait for omxplayer to close
        if self.omx<>None:
            self.mon.log(self,"sent terminate to omxdriver")
            self.omx.terminate(reason)
            self.end('killed',' end without waiting for omxplayer to finish') # end without waiting
        else:
            self.mon.log(self,"terminate, omxdriver not running")
            self.end('killed','terminate, mplayerdriver not running')


    def input_pressed(self,symbol):
        if symbol[0:4]=='omx-':
            self.control(symbol[4])
            
        elif symbol =='pause':
            self.pause()

        elif symbol=='stop':
            self.stop()
        else:
            pass


    def get_links(self):
        return self.track_params['links']

            
                
# ***************************************
# INTERNAL FUNCTIONS
# ***************************************

    # respond to normal stop
    def stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,">stop received")
        self.quit_signal=True


    #toggle pause
    def pause(self):
        if self.play_state in (VideoPlayer._PLAYING,VideoPlayer._ENDING):
            self.omx.pause()
            return True
        else:
            self.mon.log(self,"!<pause rejected")
            return False
        
    # other control when playing
    def control(self,char):
        if self.play_state==VideoPlayer._PLAYING and char not in ('q'):
            self.mon.log(self,"> send control to omx: "+ char)
            self.omx.control(char)
            return True
        else:
            self.mon.log(self,"!<control rejected")
            return False



# ***********************
# sequencing
# **********************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the omx process is not running, omx process can be initiated
         - _starting - omx process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - omx is doing its termination, controls cannot be sent
    """

    def init_play_state_machine(self):
        self.quit_signal=False
        self.play_state=VideoPlayer._CLOSED
 
    def start_play_state_machine(self,track):
        #initialise all the state machine variables
        #self.iteration = 0                             # for debugging
        self.quit_signal=False     # signal that user has pressed stop
        self.play_state=VideoPlayer._STARTING
        
        #play the selected track
        options=self.omx_audio+ " " + self.omx_volume + ' ' + self.omx_window + ' ' + self.show_params['omx-other-options']+" "
        self.omx.play(track,options)
        self.mon.log (self,'Playing track from show Id: '+ str(self.show_id))
        # and start polling for state changes
        self.tick_timer=self.canvas.after(50, self.play_state_machine)
 

    def play_state_machine(self):      
        if self.play_state == VideoPlayer._CLOSED:
            self.mon.log(self,"      State machine: " + self.play_state)
            return 
                
        elif self.play_state == VideoPlayer._STARTING:
            self.mon.log(self,"      State machine: " + self.play_state)
            
            # if omxplayer is playing the track change to play state
            if self.omx.start_play_signal==True:
                self.mon.log(self,"            <start play signal received from omx")
                self.omx.start_play_signal=False
                self.play_state=VideoPlayer._PLAYING
                self.mon.log(self,"      State machine: omx_playing started")
            self.tick_timer=self.canvas.after(50, self.play_state_machine)

        elif self.play_state == VideoPlayer._PLAYING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # service any queued stop signals
            if self.quit_signal==True:
                self.mon.log(self,"      Service stop required signal")
                self.stop_omx()
                self.quit_signal=False
                # self.play_state = VideoPlayer._ENDING
                
            # omxplayer reports it is terminating so change to ending state
            if self.omx.end_play_signal:                    
                self.mon.log(self,"            <end play signal received")
                self.mon.log(self,"            <end detected at: " + str(self.omx.video_position))
                if self.omx.end_play_reason<>'nice_day':
                    # deal with omxplayer not sending 'have a nice day'
                    self.mon.warn(self,"            <end detected at: " + str(self.omx.video_position))
                    self.mon.warn(self,"            <pexpect reports: "+self.omx.end_play_reason)
                    self.mon.warn(self,'pexpect.before  is'+self.omx.xbefore)
                self.play_state = VideoPlayer._ENDING
                self.ending_count=0
                
            self.tick_timer=self.canvas.after(200, self.play_state_machine)

        elif self.play_state == VideoPlayer._ENDING:
            self.mon.log(self,"      State machine: " + self.play_state)
            # if spawned process has closed can change to closed state
            self.mon.log (self,"      State machine : is omx process running? -  "  + str(self.omx.is_running()))
            if self.omx.is_running() ==False:
                self.mon.log(self,"            <omx process is dead")
                self.play_state = VideoPlayer._CLOSED
                self.end('normal','quit by user or system')
            else:
                self.ending_count+=1
                if self.ending_count>10:
                    # deal with omxplayer not terminating at the end of a track
                    self.mon.warn(self,"            <omxplayer failed to close at: " + str(self.omx.video_position))
                    self.mon.warn(self,'pexpect.before  is'+self.omx.xbefore)
                    self.omx.kill()
                    self.mon.warn(self,'omxplayer now  terminated ')
                    self.play_state = VideoPlayer._CLOSED
                    self.end('normal','end from omxplayer failed to terminate')
                else:
                    self.tick_timer=self.canvas.after(200, self.play_state_machine)

    def stop_omx(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,"         >stop omx received from state machine")
        if self.play_state==VideoPlayer._PLAYING:
            self.omx.stop()
            return True
        else:
            self.mon.log(self,"!<stop rejected")
            return False




# *****************
# ending the player
# *****************

    def end(self,reason,message):

            # stop the plugin
            if self.track_params['plugin']<>'':
                self.pim.stop_plugin()

            # os.system("xrefresh -display :0")
            # abort the timer
            if self.tick_timer<>None:
                self.canvas.after_cancel(self.tick_timer)
                self.tick_timer=None
            
            if reason in ('error','killed'):
                self.end_callback(reason,message)
                self=None

            else:
                # normal end so do show control and animation

                # Control concurrent shows at end
                reason,message=self.show_manager.show_control(self.track_params['show-control-end'])
                if reason =='error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                   # clear events list for this track
                    if self.track_params['animate-clear']=='yes':
                        self.ppio.clear_events_list(id(self))
                    
                    # create animation events for ending
                    reason,message=self.ppio.animate(self.animate_begin_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        self.end_callback('normal',"track has terminated or quit")
                        self=None



# *****************
# displaying things
# *****************
    def display_content(self):

        #background colour
        if  self.background_colour<>'':   
           self.canvas.config(bg=self.background_colour)
            
        # delete previous content
        self.canvas.delete('pp-content')

        # background image
        if self.background_file<>'':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self,"Video background file not found: "+ self.background_img_file)
                self.end('error',"Video background file not found")
            else:
                pil_background_img=PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                             int(self.canvas['height'])/2,
                                             image=self.background,
                                            anchor=CENTER,
                                            tag='pp-content')

        # execute the plugin if required
        if self.track_params['plugin']<>'':

            reason,message,self.track = self.pim.do_plugin(self.track,self.track_params['plugin'],)
            if reason <> 'normal':
                return reason,message

                          
        # display show text if enabled
        if self.show_params['show-text']<> '' and self.track_params['display-show-text']=='yes':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'],
                                                  tag='pp-content')


        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'],
                                                  tag='pp-content')

        # display instructions if enabled
        if self.enable_menu== True:
            self.canvas.create_text(int(self.show_params['hint-x']),
                                                    int(self.show_params['hint-y']),
                                                  text=self.show_params['hint-text'],
                                                  fill=self.show_params['hint-colour'],
                                                font=self.show_params['hint-font'],
                                                anchor=NW,
                                                tag='pp-content')

        self.canvas.tag_raise('pp-click-area')
        self.canvas.update_idletasks( )
        return 'normal',''


# ****************
# utilities
# *****************

    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Background image is "+ track_file)
        return track_file

# original _
# warp _ or xy2


    def parse_window(self,line):
        
            fields = line.split()
            # check there is a command field
            if len(fields) < 1:
                    return 'error','no type field','',False,0,0,0,0
                
            # deal with original which has 1
            if fields[0]=='original':
                if len(fields) <> 1:
                        return 'error','number of fields for original','',False,0,0,0,0    
                return 'normal','',fields[0],False,0,0,0,0


            #deal with warp which has 1 or 5  arguments
            # check basic syntax
            if  fields[0] <>'warp':
                    return 'error','not a valid type','',False,0,0,0,0
            if len(fields) not in (1,5):
                    return 'error','wrong number of coordinates for warp','',False,0,0,0,0

            # deal with window coordinates    
            if len(fields) == 5:
                #window is specified
                if not (fields[1].isdigit() and fields[2].isdigit() and fields[3].isdigit() and fields[4].isdigit()):
                    return 'error','coordinates are not positive integers','',False,0,0,0,0
                has_window=True
                return 'normal','',fields[0],has_window,int(fields[1]),int(fields[2]),int(fields[3]),int(fields[4])
            else:
                # fullscreen
                has_window=True
                return 'normal','',fields[0],has_window,0,0,self.canvas['width'],self.canvas['height']
Ejemplo n.º 7
0
class BrowserPlayer:


    #state constants
    _CLOSED = "player_closed"    #probably will not exist
    _STARTING = "player_starting"  #uzbl beinf loaded and fifo created
    _WAITING = "wait for timeout" # waiting for browser to appear on the screen
    _PLAYING = "player_playing"  #track is playing to the screen
    _ENDING = "player_ending"  #track is in the process of ending due to quit or duration exceeded




# ***************************************
# EXTERNAL COMMANDS
# ***************************************

    def __init__(self,
                        show_id,
                         root,
                        canvas,
                        show_params,
                        track_params,
                     pp_dir,
                        pp_home,
                        pp_profile):

        self.mon=Monitor()
        self.mon.on()

        
        #instantiate arguments
        self.show_id=show_id
        self.root=root,
        self.canvas = canvas
        self.show_params=show_params  
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile

        # get duration limit (secs ) from profile
        if self.track_params['duration']<>"":
            self.duration= int(self.track_params['duration'])
        else:
            self.duration= int(self.show_params['duration'])
        self.duration_limit=20*self.duration


        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>"":
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']

        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # could put instance generation in play, not sure which is better.
        self.bplayer=uzblDriver(self.canvas)
        self.command_timer=None
        self.tick_timer=None
        self.init_play_state_machine()



    def play(self, track,
                     showlist,
                     end_callback,
                     ready_callback,
                     enable_menu=False):

        #instantiate arguments
        self.track=track
        self.showlist=showlist
        self.end_callback=end_callback         # callback when finished
        self.ready_callback=ready_callback   #callback when ready to play
        self.enable_menu=enable_menu

        # callback to the calling object to e.g remove egg timer.
        if self.ready_callback<>None:
            self.ready_callback()

        # create an  instance of showmanager so we can control concurrent shows
        self.show_manager=ShowManager(self.show_id,self.showlist,self.show_params,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home)

        #web window                  
        if self.track_params['web-window']<>'':
            self.web_window= self.track_params['web-window']
        else:
            self.web_window= self.show_params['web-window']

        reason,message,command,has_window,x1,y1,x2,y2=self.parse_window(self.web_window)
        if reason =='error':
            self.mon.err(self,'web window error: '+'  ' + message + ' in ' + self.web_window)
            self.end_callback(reason,message)
            self=None
        else:
            #deal with web_window
            if has_window==False:
                self.geometry = ' --geometry=maximized '
            else:
                width=x2-x1
                height=y2-y1
                self.geometry = "--geometry=%dx%d%+d%+d "  % (width,height,x1,y1)

            # get browser commands
            reason,message=self.parse_commands(self.track_params['browser-commands'])
            if reason != 'normal':
                self.mon.err(self,message)
                self.end_callback(reason,message)
                self=None
            else:
            
                # Control other shows at beginning
                reason,message=self.show_manager.show_control(self.track_params['show-control-begin'])
                if reason == 'error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                    #display content
                    reason,message=self.display_content()
                    if reason == 'error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        # create animation events
                        reason,message=self.ppio.animate(self.animate_begin_text,id(self))
                        if reason=='error':
                            self.mon.err(self,message)
                            self.end_callback(reason,message)
                            self=None
                        else:     
                            # start playing the track.
                            self.start_play_state_machine()

    def terminate(self,reason):
        """
        terminate the  player in special circumstances
        normal user termination if by key_pressed 'stop'
        reason will be killed or error
        """
        # circumvents state machine to terminate lower level and then itself.
        if self.bplayer<>None:
            self.mon.log(self,"sent terminate to uzbldriver")
            self.bplayer.terminate(reason)
            self.end('killed',' end without waiting for uzbl to finish') # end without waiting
        else:
            self.mon.log(self,"terminate, uzbldriver not running")
            self.end('killed','terminate, uzbldriver not running')

    def get_links(self):
        return self.track_params['links']

    def input_pressed(self,symbol):
        # print symbol, symbol[0:5]
        if symbol[0:5]=='uzbl-':
            self.control(symbol[5:])
            
        elif symbol == 'pause':
            self.pause()

        elif symbol=='stop':
            self.stop()


        
# ***************************************
# INTERNAL FUNCTIONS
# ***************************************

    #browser do not do pause
    def pause(self):
        self.mon.log(self,"!<pause rejected")
        return False
        
    # other control when playing, not currently used
    def control(self,char):
        if self.play_state==BrowserPlayer._PLAYING and char not in ('exit'):
            self.mon.log(self,"> send control to uzbl:"+ char)
            self.bplayer.control(char)
            return True
        else:
            self.mon.log(self,"!<control rejected")
            return False

    # respond to normal stop
    def stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,">stop received")
        self.quit_signal=True

         
      
# ***************************************
#  sequencing
# ***************************************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the mplayer process is not running, mplayer process can be initiated
         - _starting - mplayer process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - mplayer is doing its termination, controls cannot be sent
    """

    def init_play_state_machine(self):
        self.quit_signal=False
        self.play_state=BrowserPlayer._CLOSED
 
    def start_play_state_machine(self):
        #initialise all the state machine variables
        self.duration_count = 0
        self.quit_signal=False     # signal that user has pressed stop

        #play the track
        self.bplayer.play(self.track,self.geometry)
        self.mon.log (self,'Playing track from show Id: '+ str(self.show_id))
        self.play_state=BrowserPlayer._STARTING
        
        # and start polling for state changes and count duration
        self.tick_timer=self.canvas.after(50, self.play_state_machine)


    def play_state_machine(self):

        if self.play_state == BrowserPlayer._CLOSED:
            self.mon.log(self,"      State machine: " + self.play_state)
            return 
                
        elif self.play_state == BrowserPlayer._STARTING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            
            # if uzbl fifo is available can send comands to uzbl but change to wait state to wait for it to appear on screen
            if self.bplayer.start_play_signal==True:
                self.mon.log(self,"            <fifo available signal received from uzbl")
                self.bplayer.start_play_signal=False
                self.play_state=BrowserPlayer._WAITING
                # get rid of status bar
                self.bplayer.control('set show_status = 0')
                # and get ready to wait for browser to appear
                self.wait_count= 50   # 10 seconds at 200mS steps 
                self.mon.log(self,"      State machine: uzbl process alive")
                
            self.tick_timer=self.canvas.after(200, self.play_state_machine)


        elif self.play_state == BrowserPlayer._WAITING:
            if self.wait_count==0:
                # set state to playing
                self.play_state = BrowserPlayer._PLAYING
                # and start executing the browser commands
                self.play_commands()
                self.mon.log(self,"      State machine: uzbl_playing started")
                
            self.wait_count -=1
            self.tick_timer=self.canvas.after(200, self.play_state_machine)

        elif self.play_state == BrowserPlayer._PLAYING:
            self.duration_count+=1
            # self.mon.log(self,"      State machine: " + self.play_state)
            
            # service any queued stop signals and test duration count
            if self.quit_signal==True or (self.duration_limit>0 and self.duration_count>self.duration_limit):
                self.mon.log(self,"      Service stop required signal or timeout")
                # self.quit_signal=False
                self.stop_bplayer()
                self.play_state = BrowserPlayer._ENDING

            # uzbl reports it is terminating so change to ending state
            if self.bplayer.end_play_signal:                    
                self.mon.log(self,"            <end play signal received")
                self.play_state = BrowserPlayer._ENDING
            self.tick_timer=self.canvas.after(50, self.play_state_machine)

        elif self.play_state == BrowserPlayer._ENDING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # if spawned process has closed can change to closed state
            # self.mon.log (self,"      State machine : is luakit process running? -  "  + str(self.bplayer.is_running()))
            if self.bplayer.is_running() ==False:
                self.mon.log(self,"            <uzbl process is dead")
                if self.quit_signal==True:
                    self.quit_signal=False
                self.play_state = BrowserPlayer._CLOSED
                self.end('normal','quit required or timeout')
            else:
                self.tick_timer=self.canvas.after(50, self.play_state_machine)
                


    def stop_bplayer(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,"         >send stop to uzbl driver")
        if self.play_state==BrowserPlayer._PLAYING:
            self.bplayer.stop()
            return True
        else:
            self.mon.log(self,"!<stop rejected")
            return False

# *****************
# ending the player
# *****************

    def end(self,reason,message):
            # stop the plugin
            if self.pim<>None:
                self.pim.stop_plugin()

            # abort the timers
            if self.tick_timer<>None:
                self.canvas.after_cancel(self.tick_timer)
                self.tick_timer=None
            if self.command_timer<>None:
                self.canvas.after_cancel(self.command_timer)
                self.tick_timer=None
            # clean up and fifos and sockets left by uzbl
            os.system('rm -f  /tmp/uzbl_*')
            if reason in ('error','killed'):
                self.end_callback(reason,message)
                self=None

            else:
                # normal end so do show control and animation

                # Control concurrent shows at end
                reason,message=self.show_manager.show_control(self.track_params['show-control-end'])
                if reason =='error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                   # clear events list for this track
                    if self.track_params['animate-clear']=='yes':
                        self.ppio.clear_events_list(id(self))
                    
                    # create animation events for ending
                    reason,message=self.ppio.animate(self.animate_end_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        self.end_callback('normal',"track has terminated or quit")
                        self=None



# *****************
# displaying things
# *****************
            
    def display_content(self):
       

        self.canvas.delete('pp-content')


        #background colour
        if  self.background_colour<>'':
            self.canvas.config(bg=self.background_colour)
            
        if self.background_file<>'':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self,"Audio background file not found: "+ self.background_img_file)
                self.end('error',"Audio background file not found")
            else:
                pil_background_img=PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                              int(self.canvas['height'])/2,
                                              image=self.background,
                                              anchor=CENTER,
                                                tag='pp-content')

        # execute the plugin if required
        if self.track_params['plugin']<>'':

            reason,message,self.track = self.pim.do_plugin(self.track,self.track_params['plugin'],)
            if reason <> 'normal':
                return reason,message

                
        # display hint text if enabled
       
        if self.enable_menu== True:
            self.canvas.create_text(int(self.show_params['hint-x']),
                                                    int(self.show_params['hint-y']),
                                                  text=self.show_params['hint-text'],
                                                  fill=self.show_params['hint-colour'],
                                                font=self.show_params['hint-font'],
                                                    anchor=NW,
                                                tag='pp-content')

            
        # display show text if enabled
        if self.show_params['show-text']<> ''and self.track_params['display-show-text']=='yes':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'],
                                                tag='pp-content')
            
        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'],
                                                tag='pp-content')

        self.mon.log(self,"Displayed background and text ")

        self.canvas.tag_raise('pp-click-area')
        
        self.canvas.update_idletasks( )

        return 'normal',''


     
# *******************   
# browser commands
# ***********************

    def parse_commands(self,command_text):
        self.command_list=[]
        lines = command_text.split('\n')
        for line in lines:
            if line.strip()=="":
                continue
            reason,entry=self.parse_command(line)
            if reason != 'normal':
                return 'error',entry
            self.command_list.append(copy.deepcopy(entry))
        # print self.command_list
        return 'normal',''

    def parse_command(self,line):
        fields = line.split()
        if fields[0]=='uzbl':
            # print fields[0], line[4:]
            return  'normal',[fields[0],line[4:]]
        
        if len(fields) not in (1,2):
            return 'error',"incorrect number of fields in command: " + line
        command=fields[0]
        arg=''
        if command not in ('load','refresh','wait','exit','loop'):
            return 'error','unknown command: '+ command
            
        if command in ('refresh','exit','loop') and len(fields)<>1:
            return 'error','incorrect number of fields for '+ command + 'in: ' + line
            
        if command == 'load':
            if len(fields)<>2:
                return 'error','incorrect number of fields for '+ command + 'in: ' + line
            else:
                arg = fields[1]


        if command == 'wait':
            if len(fields)<>2:
                return 'error','incorrect number of fields for '+ command + 'in: ' + line
            else:
                arg = fields[1]
                if not arg.isdigit():return 'error','Argument for Wait is not 0 or positive number in: ' + line

        return 'normal',[command,arg]


        
    def play_commands(self):
        if len(self.command_list)==0:
            return
        self.loop=0
        self.command_index=0
        self.canvas.after(100,self.execute_command)

        
    def execute_command(self):
        entry=self.command_list[self.command_index]
        command=entry[0]
        arg=entry[1]
        if self.command_index==len(self.command_list)-1:
            self.command_index=self.loop
        else:
            self.command_index+=1
            
        # execute command
        if command == 'load':
            #self.canvas.focus_force()
            #self.root.lower()
            file=self.complete_path(arg)
            self.bplayer.control('uri '+ file)
            self.command_timer=self.canvas.after(10,self.execute_command)
        elif command == 'refresh':
            self.bplayer.control('reload_ign_cache')
            self.command_timer=self.canvas.after(10,self.execute_command)
        elif command == 'wait':
            self.command_timer=self.canvas.after(1000*int(arg),self.execute_command)        
        elif  command=='exit':
            self.quit_signal=True
        elif command=='loop':
            self.loop=self.command_index
            self.command_timer=self.canvas.after(10,self.execute_command)
        elif command=='uzbl':
            self.bplayer.control(arg)
            self.command_timer=self.canvas.after(10,self.execute_command)

        
        
        
# *****************
# utilities
# *****************

    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Background image is "+ track_file)
        return track_file     
            





    def parse_window(self,line):
            # parses warp _ or xy2
            
            fields = line.split()
            # check there is a command field
            if len(fields) < 1:
                    return 'error','no type field','',False,0,0,0,0
                

            #deal with warp which has 1 or 5  arguments
            # check basic syntax
            if  fields[0] <>'warp':
                    return 'error','not a valid type','',False,0,0,0,0
            if len(fields) not in (1,5):
                    return 'error','wrong number of coordinates for warp','',False,0,0,0,0

            # deal with window coordinates    
            if len(fields) == 5:
                #window is specified
                if not (fields[1].isdigit() and fields[2].isdigit() and fields[3].isdigit() and fields[4].isdigit()):
                    return 'error','coordinates are not positive integers','',False,0,0,0,0
                has_window=True
                return 'normal','',fields[0],has_window,int(fields[1]),int(fields[2]),int(fields[3]),int(fields[4])
            else:
                # fullscreen
                has_window=False
                return 'normal','',fields[0],has_window,0,0,0,0
Ejemplo n.º 8
0
class PiPresents:

    def __init__(self):
        
        self.pipresents_issue="1.2"
        self.nonfull_window_width = 0.6 # proportion of width
        self.nonfull_window_height= 0.6 # proportion of height
        self.nonfull_window_x = 0 # position of top left corner
        self.nonfull_window_y=0   # position of top left corner
        
        StopWatch.global_enable=False

#****************************************
# Initialisation
# ***************************************
        # get command line options
        self.options=command_options()

        # get pi presents code directory
        pp_dir=sys.path[0]
        self.pp_dir=pp_dir
        
        if not os.path.exists(pp_dir+"/pipresents.py"):
            tkMessageBox.showwarning("Pi Presents","Bad Application Directory")
            exit()

        
        #Initialise logging
        Monitor.log_path=pp_dir
        self.mon=Monitor()
        self.mon.on()
        if self.options['debug']==True:
            Monitor.global_enable=True
        else:
            Monitor.global_enable=False
 
        self.mon.log (self, "Pi Presents is starting")
        self.mon.log (self," OS and separator:" + os.name +'  ' + os.sep)
        self.mon.log(self,"sys.path[0] -  location of code: "+sys.path[0])
        # self.mon.log(self,"os.getenv('HOME') -  user home directory (not used): " + os.getenv('HOME'))
        # self.mon.log(self,"os.path.expanduser('~') -  user home directory: " + os.path.expanduser('~'))

        # optional other classes used
        self.ppio=None
        self.tod=None
         
        #get profile path from -p option
        if self.options['profile']<>"":
            self.pp_profile_path="/pp_profiles/"+self.options['profile']
        else:
            self.pp_profile_path = "/pp_profiles/pp_profile"
        
       #get directory containing pp_home from the command,
        if self.options['home'] =="":
            home = os.path.expanduser('~')+ os.sep+"pp_home"
        else:
            home = self.options['home'] + os.sep+ "pp_home"         
        self.mon.log(self,"pp_home directory is: " + home)
        
        #check if pp_home exists.
        # try for 10 seconds to allow usb stick to automount
        # fall back to pipresents/pp_home
        self.pp_home=pp_dir+"/pp_home"
        found=False
        for i in range (1, 10):
            self.mon.log(self,"Trying pp_home at: " + home +  " (" + str(i)+')')
            if os.path.exists(home):
                found=True
                self.pp_home=home
                break
            time.sleep (1)
        if found==True:
            self.mon.log(self,"Found Requested Home Directory, using pp_home at: " + home)
        else:    
            self.mon.log(self,"FAILED to find requested home directory, using default to display error message: " + self.pp_home)


        #check profile exists, if not default to error profile inside pipresents
        self.pp_profile=self.pp_home+self.pp_profile_path
        if os.path.exists(self.pp_profile):
            self.mon.log(self,"Found Requested profile - pp_profile directory is: " + self.pp_profile)
        else:
            self.pp_profile=pp_dir+"/pp_home/pp_profiles/pp_profile"   
            self.mon.log(self,"FAILED to find requested profile, using default to display error message: pp_profile")
        
        if self.options['verify']==True:
            val =Validator()
            if  val.validate_profile(None,pp_dir,self.pp_home,self.pp_profile,self.pipresents_issue,False) == False:
                tkMessageBox.showwarning("Pi Presents","Validation Failed")
                exit()
                
        # open the resources
        self.rr=ResourceReader()
        # read the file, done once for all the other classes to use.
        if self.rr.read(pp_dir,self.pp_home,self.pp_profile)==False:
            self.end('error','cannot find resources.cfg')            

        #initialise and read the showlist in the profile
        self.showlist=ShowList()
        self.showlist_file= self.pp_profile+ "/pp_showlist.json"
        if os.path.exists(self.showlist_file):
            self.showlist.open_json(self.showlist_file)
        else:
            self.mon.err(self,"showlist not found at "+self.showlist_file)
            self.end('error','showlist not found')

        # check profile and Pi Presents issues are compatible
        if float(self.showlist.sissue())<>float(self.pipresents_issue):
            self.mon.err(self,"Version of profile " + self.showlist.sissue() + " is not  same as Pi Presents, must exit")
            self.end('error','wrong version of profile')
 
        # get the 'start' show from the showlist
        index = self.showlist.index_of_show('start')
        if index >=0:
            self.showlist.select(index)
            self.starter_show=self.showlist.selected_show()
        else:
            self.mon.err(self,"Show [start] not found in showlist")
            self.end('error','start show not found')

        
# ********************
# SET UP THE GUI
# ********************
        #turn off the screenblanking and saver
        if self.options['noblank']==True:
            call(["xset","s", "off"])
            call(["xset","s", "-dpms"])

        self.root=Tk()   
       
        self.title='Pi Presents - '+ self.pp_profile
        self.icon_text= 'Pi Presents'
        self.root.title(self.title)
        self.root.iconname(self.icon_text)
        self.root.config(bg='black')
        
        # get size of the screen
        self.screen_width = self.root.winfo_screenwidth()
        self.screen_height = self.root.winfo_screenheight()

        # set window dimensions and decorations
        if self.options['fullscreen']==True:

            self.root.attributes('-fullscreen', True)
            os.system('unclutter &')
            self.window_width=self.screen_width
            self.window_height=self.screen_height
            self.window_x=0
            self.window_y=0  
            self.root.geometry("%dx%d%+d%+d"  % (self.window_width,self.window_height,self.window_x,self.window_y))
            self.root.attributes('-zoomed','1')
        else:
            self.window_width=int(self.screen_width*self.nonfull_window_width)
            self.window_height=int(self.screen_height*self.nonfull_window_height)
            self.window_x=self.nonfull_window_x
            self.window_y=self.nonfull_window_y
            self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y))

            
        #canvas covers the whole window
        self.canvas_height=self.screen_height
        self.canvas_width=self.screen_width
        
        # make sure focus is set.
        self.root.focus_set()

        #define response to main window closing.
        self.root.protocol ("WM_DELETE_WINDOW", self.exit_pressed)

        #setup a canvas onto which will be drawn the images or text
        self.canvas = Canvas(self.root, bg='black')

        self.canvas.config(height=self.canvas_height,
                                       width=self.canvas_width,
                                       highlightthickness=0)
        # self.canvas.pack()
        self.canvas.place(x=0,y=0)

        self.canvas.focus_set()

                
# ****************************************
# INITIALISE THE INPUT DRIVERS
# ****************************************

        # looks after bindings between symbolic names and internal operations
        controlsmanager=ControlsManager()
        if controlsmanager.read(pp_dir,self.pp_home,self.pp_profile)==False:
                self.end('error','cannot find or error in controls.cfg.cfg')
        else:
            controlsmanager.parse_defaults()

        # each driver takes a set of inputs, binds them to symboic names
        # and sets up a callback which returns the symbolic name when an input event occurs/

        # use keyboard driver to bind keys to symbolic names and to set up callback
        kbd=KbdDriver()
        if kbd.read(pp_dir,self.pp_home,self.pp_profile)==False:
                self.end('error','cannot find or error in keys.cfg')
        kbd.bind_keys(self.root,self.input_pressed)

        self.sr=ScreenDriver()
        # read the screen click area config file
        if self.sr.read(pp_dir,self.pp_home,self.pp_profile)==False:
            self.end('error','cannot find screen.cfg')

        # create click areas on the canvas, must be polygon as outline rectangles are not filled as far as find_closest goes
        reason,message = self.sr.make_click_areas(self.canvas,self.input_pressed)
        if reason=='error':
            self.mon.err(self,message)
            self.end('error',message)


# ****************************************
# INITIALISE THE APPLICATION AND START
# ****************************************
        self.shutdown_required=False
        
        #kick off GPIO if enabled by command line option
        if self.options['gpio']==True:
            from pp_gpio import PPIO
            # initialise the GPIO
            self.ppio=PPIO()
            # PPIO.gpio_enabled=False
            if self.ppio.init(pp_dir,self.pp_home,self.pp_profile,self.canvas,50,self.gpio_pressed)==False:
                self.end('error','gpio error')
                
            # and start polling gpio
            self.ppio.poll()

        #kick off the time of day scheduler
        self.tod=TimeOfDay()
        self.tod.init(pp_dir,self.pp_home,self.canvas,500)
        self.tod.poll()


        # Create list of start shows initialise them and then run them
        self.run_start_shows()

        #start tkinter
        self.root.mainloop( )



# *********************
#  RUN START SHOWS
# ********************   
    def run_start_shows(self):
        #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.all_shows_ended_callback)

        #parse the start shows field and start the initial shows       
        start_shows_text=self.starter_show['start-show']
        self.show_manager.start_initial_shows(start_shows_text)

    #callback from ShowManager when all shows have ended
    def all_shows_ended_callback(self,reason,message,force_shutdown):
        self.mon.log(self,"All shows ended, so terminate Pi Presents")
        if force_shutdown==True:
            self.shutdown_required=True
            self.mon.log(self,"shutdown forced by profile")  
            self.terminate('killed')
        else:
            self.end(reason,message)


# *********************
# User inputs
# ********************

    #gpio callback - symbol provided by gpio
    def gpio_pressed(self,index,symbol,edge):
        self.mon.log(self, "GPIO Pressed: "+ symbol)
        self.input_pressed(symbol,edge,'gpio')


    
    # all input events call this callback with a symbolic name.              
    def input_pressed(self,symbol,edge,source):
        self.mon.log(self,"input received: "+symbol)
        if symbol=='pp-exit':
            self.exit_pressed()
        elif symbol=='pp-shutdown':
            self.shutdown_pressed('delay')
        elif symbol=='pp-shutdownnow':
            self.shutdown_pressed('now')
        else:
            for show in self.show_manager.shows:
                show_obj=show[ShowManager.SHOW_OBJ]
                if show_obj<>None:
                    show_obj.input_pressed(symbol,edge,source)


# **************************************
# respond to exit inputs by terminating
# **************************************

    def shutdown_pressed(self, when):
        if when=='delay':
            self.root.after(5000,self.on_shutdown_delay)
        else:
            self.shutdown_required=True
            self.exit_pressed()           

    def on_shutdown_delay(self):
        if self.ppio.shutdown_pressed():
            self.shutdown_required=True
            self.exit_pressed()

         
    def exit_pressed(self):
        self.mon.log(self, "kill received from user")
        #terminate any running shows and players     
        self.mon.log(self,"kill sent to shows")   
        self.terminate('killed')


     # kill or error
    def terminate(self,reason):
        needs_termination=False
        for show in self.show_manager.shows:
            if show[ShowManager.SHOW_OBJ]<>None:
                needs_termination=True
                self.mon.log(self,"Sent terminate to show "+ show[ShowManager.SHOW_REF])
                show[ShowManager.SHOW_OBJ].terminate(reason)
        if needs_termination==False:
            self.end(reason,'terminate - no termination of lower levels required')


# ******************************
# Ending Pi Presents after all the showers and players are closed
# **************************

    def end(self,reason,message):
        self.mon.log(self,"Pi Presents ending with message: " + reason + ' ' + message)
        if reason=='error':
            self.tidy_up()
            self.mon.log(self, "exiting because of error")
            #close logging files 
            self.mon.finish()
            exit()            
        else:
            self.tidy_up()
            self.mon.log(self,"no error - exiting normally")
            #close logging files 
            self.mon.finish()
            if self.shutdown_required==True:
                call(['sudo', 'shutdown', '-h', '-t 5','now'])
                exit()
            else:
                exit()


    
    # tidy up all the peripheral bits of Pi Presents
    def tidy_up(self):
        #turn screen blanking back on
        if self.options['noblank']==True:
            call(["xset","s", "on"])
            call(["xset","s", "+dpms"])
            
        # tidy up gpio
        if self.options['gpio']==True and self.ppio<>None:
            self.ppio.terminate()
            
        #tidy up time of day scheduler
        if self.tod<>None:
            self.tod.terminate()



# *****************************
# utilitities
# ****************************

    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
Ejemplo n.º 9
0
class VideoPlayer:
    """ plays a track using omxplayer
        See pp_imageplayer for common software design description
    """

    _CLOSED = "omx_closed"    #probably will not exist
    _STARTING = "omx_starting"  #track is being prepared
    _PLAYING = "omx_playing"  #track is playing to the screen, may be paused
    _ENDING = "omx_ending"  #track is in the process of ending due to quit or end of track


# ***************************************
# EXTERNAL COMMANDS
# ***************************************

    def __init__(self,
                         show_id,
                         root,
                        canvas,
                        show_params,
                        track_params ,
                         pp_dir,
                        pp_home,
                        pp_profile):

        self.mon=Monitor()
        self.mon.on()
        
        #instantiate arguments
        self.show_id=show_id
        self.root=root
        self.canvas = canvas
        self.show_params=show_params
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile


        # get config from medialist if there.
        if self.track_params['omx-audio']<>"":
            self.omx_audio= self.track_params['omx-audio']
        else:
            self.omx_audio= self.show_params['omx-audio']
        if self.omx_audio<>"": self.omx_audio= "-o "+ self.omx_audio
        
        if self.track_params['omx-volume']<>"":
            self.omx_volume= self.track_params['omx-volume']
        else:
            self.omx_volume= self.show_params['omx-volume']
        if self.omx_volume<>"":
            self.omx_volume= "--vol "+ str(int(self.omx_volume)*100) + ' '

        if self.track_params['omx-window']<>'':
            self.omx_window= self.track_params['omx-window']
        else:
            self.omx_window= self.show_params['omx-window']


        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>"":
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']
        
        self.centre_x = int(self.canvas['width'])/2
        self.centre_y = int(self.canvas['height'])/2
        
        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()        
        
        # could put instance generation in play, not sure which is better.
        self.omx=OMXDriver(self.canvas)
        self.tick_timer=None
        self.init_play_state_machine()



    def play(self, track,
                     showlist,
                     end_callback,
                     ready_callback,
                     enable_menu=False):
                         
        #instantiate arguments
        self.track=track
        self.showlist=showlist
        self.ready_callback=ready_callback   #callback when ready to play
        self.end_callback=end_callback         # callback when finished
        self.enable_menu = enable_menu
 
        # callback to the calling object to e.g remove egg timer and enable click areas.
        if self.ready_callback<>None:
            self.ready_callback()

        # create an  instance of showmanager so we can control concurrent shows
        self.show_manager=ShowManager(self.show_id,self.showlist,self.show_params,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home)

        #set up video window
        reason,message,comand,has_window,x1,y1,x2,y2= self.parse_window(self.omx_window)
        if reason =='error':
            self.mon.err(self,'omx window error: ' + message + ' in ' + self.omx_window)
            self.end_callback(reason,message)
        else:
            if has_window==True:
                self.omx_window= '--win " '+ str(x1) +  ' ' + str(y1) + ' ' + str(x2) + ' ' + str(y2) + ' " '
            else:
                self.omx_window=''

             # Control other shows at beginning
            reason,message=self.show_manager.show_control(self.track_params['show-control-begin'])
            if reason in ('error','killed'):
                self.end_callback(reason,message)
                self=None
            else:      
                #display content
                reason,message=self.display_content()
                if reason == 'error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                    # create animation events
                    reason,message=self.ppio.animate(self.animate_begin_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        # start playing the video.
                        if self.play_state == VideoPlayer._CLOSED:
                            self.mon.log(self,">play track received")
                            self.start_play_state_machine(self.track)
                        else:
                            self.mon.err(self,'play track rejected')
                            self.end_callback('error','play track rejected')
                            self=None

    def terminate(self,reason):
        # circumvents state machine and does not wait for omxplayer to close
        if self.omx<>None:
            self.mon.log(self,"sent terminate to omxdriver")
            self.omx.terminate(reason)
            self.end('killed',' end without waiting for omxplayer to finish') # end without waiting
        else:
            self.mon.log(self,"terminate, omxdriver not running")
            self.end('killed','terminate, mplayerdriver not running')


    def input_pressed(self,symbol):
        if symbol[0:4]=='omx-':
            self.control(symbol[4])
            
        elif symbol =='pause':
            self.pause()

        elif symbol=='stop':
            self.stop()
        else:
            pass


    def get_links(self):
        return self.track_params['links']

            
                
# ***************************************
# INTERNAL FUNCTIONS
# ***************************************

    # respond to normal stop
    def stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,">stop received")
        self.quit_signal=True


    #toggle pause
    def pause(self):
        if self.play_state in (VideoPlayer._PLAYING,VideoPlayer._ENDING):
            self.omx.pause()
            return True
        #NIK
        # for pausing video after first frame
        elif self.play_state == VideoPlayer._STARTING:
            self.omx.delayed_pause = True
            return True
        #NIK
        else:
            self.mon.log(self,"!<pause rejected")
            return False
        
    # other control when playing
    def control(self,char):
        if self.play_state==VideoPlayer._PLAYING and char not in ('q'):
            self.mon.log(self,"> send control to omx: "+ char)
            self.omx.control(char)
            return True
        else:
            self.mon.log(self,"!<control rejected")
            return False



# ***********************
# sequencing
# **********************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the omx process is not running, omx process can be initiated
         - _starting - omx process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - omx is doing its termination, controls cannot be sent
    """

    def init_play_state_machine(self):
        self.quit_signal=False
        self.play_state=VideoPlayer._CLOSED
 
    def start_play_state_machine(self,track):
        #initialise all the state machine variables
        #self.iteration = 0                             # for debugging
        self.quit_signal=False     # signal that user has pressed stop
        self.play_state=VideoPlayer._STARTING
        
        #play the selected track
        options=self.omx_audio+ " " + self.omx_volume + ' ' + self.omx_window + ' ' + self.show_params['omx-other-options']+" "
        # NIK ADDITION
        # adding subtitles file for video
        if 'omx-subtitles' in self.track_params and self.track_params['omx-subtitles'] <> '':
            subtitles_full_path = self.complete_path(self.track_params['omx-subtitles'])
            if os.path.exists (subtitles_full_path):
                options += '--font-size 40 --subtitles "' + subtitles_full_path + '" '

        if 'omx-subtitles-numlines' in self.track_params and self.track_params['omx-subtitles-numlines'] <> '':
            options += '--lines ' + self.track_params['omx-subtitles-numlines'] + ' '
        # END NIK ADDITION
        self.omx.play(track,options)
        self.mon.log (self,'Playing track from show Id: '+ str(self.show_id))
        # and start polling for state changes
        self.tick_timer=self.canvas.after(50, self.play_state_machine)
 

    def play_state_machine(self):      
        if self.play_state == VideoPlayer._CLOSED:
            self.mon.log(self,"      State machine: " + self.play_state)
            return 
                
        elif self.play_state == VideoPlayer._STARTING:
            self.mon.log(self,"      State machine: " + self.play_state)
            
            # if omxplayer is playing the track change to play state
            if self.omx.start_play_signal==True:
                self.mon.log(self,"            <start play signal received from omx")
                self.omx.start_play_signal=False
                self.play_state=VideoPlayer._PLAYING
                self.mon.log(self,"      State machine: omx_playing started")

            self.tick_timer=self.canvas.after(50, self.play_state_machine)

        elif self.play_state == VideoPlayer._PLAYING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # service any queued stop signals
            if self.quit_signal==True:
                self.mon.log(self,"      Service stop required signal")
                self.stop_omx()
                self.quit_signal=False
                # self.play_state = VideoPlayer._ENDING
                
            # omxplayer reports it is terminating so change to ending state
            if self.omx.end_play_signal:                    
                self.mon.log(self,"            <end play signal received")
                self.mon.log(self,"            <end detected at: " + str(self.omx.video_position))
                if self.omx.end_play_reason<>'nice_day':
                    # deal with omxplayer not sending 'have a nice day'
                    self.mon.warn(self,"            <end detected at: " + str(self.omx.video_position))
                    self.mon.warn(self,"            <pexpect reports: "+self.omx.end_play_reason)
                    self.mon.warn(self,'pexpect.before  is'+self.omx.xbefore)
                self.play_state = VideoPlayer._ENDING
                self.ending_count=0
                
            self.tick_timer=self.canvas.after(200, self.play_state_machine)

        elif self.play_state == VideoPlayer._ENDING:
            self.mon.log(self,"      State machine: " + self.play_state)
            # if spawned process has closed can change to closed state
            self.mon.log (self,"      State machine : is omx process running? -  "  + str(self.omx.is_running()))
            if self.omx.is_running() ==False:
                self.mon.log(self,"            <omx process is dead")
                self.play_state = VideoPlayer._CLOSED
                self.end('normal','quit by user or system')
            else:
                self.ending_count+=1
                if self.ending_count>10:
                    # deal with omxplayer not terminating at the end of a track
                    self.mon.warn(self,"            <omxplayer failed to close at: " + str(self.omx.video_position))
                    self.mon.warn(self,'pexpect.before  is'+self.omx.xbefore)
                    self.omx.kill()
                    self.mon.warn(self,'omxplayer now  terminated ')
                    self.play_state = VideoPlayer._CLOSED
                    self.end('normal','end from omxplayer failed to terminate')
                else:
                    self.tick_timer=self.canvas.after(200, self.play_state_machine)

    def stop_omx(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,"         >stop omx received from state machine")
        if self.play_state==VideoPlayer._PLAYING:
            self.omx.stop()
            return True
        else:
            self.mon.log(self,"!<stop rejected")
            return False




# *****************
# ending the player
# *****************

    def end(self,reason,message):

            # stop the plugin
            if self.track_params['plugin']<>'':
                self.pim.stop_plugin()

            # os.system("xrefresh -display :0")
            # abort the timer
            if self.tick_timer<>None:
                self.canvas.after_cancel(self.tick_timer)
                self.tick_timer=None
            
            if reason in ('error','killed'):
                self.end_callback(reason,message)
                self=None

            else:
                # normal end so do show control and animation

                # Control concurrent shows at end
                reason,message=self.show_manager.show_control(self.track_params['show-control-end'])
                if reason =='error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                   # clear events list for this track
                    if self.track_params['animate-clear']=='yes':
                        self.ppio.clear_events_list(id(self))
                    
                    # create animation events for ending
                    reason,message=self.ppio.animate(self.animate_end_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        self.end_callback('normal',"track has terminated or quit")
                        self=None



# *****************
# displaying things
# *****************
    def display_content(self):

        #background colour
        if  self.background_colour<>'':   
           self.canvas.config(bg=self.background_colour)
            
        # delete previous content
        self.canvas.delete('pp-content')

        # background image
        if self.background_file<>'':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self,"Video background file not found: "+ self.background_img_file)
                self.end('error',"Video background file not found")
            else:
                pil_background_img=PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                             int(self.canvas['height'])/2,
                                             image=self.background,
                                            anchor=CENTER,
                                            tag='pp-content')

        # execute the plugin if required
        if self.track_params['plugin']<>'':

            reason,message,self.track = self.pim.do_plugin(self.track,self.track_params['plugin'],)
            if reason <> 'normal':
                return reason,message

                          
        # display show text if enabled
        if self.show_params['show-text']<> '' and self.track_params['display-show-text']=='yes':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'],
                                                  tag='pp-content')


        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'],
                                                  tag='pp-content')

        # display instructions if enabled
        if self.enable_menu== True:
            self.canvas.create_text(int(self.show_params['hint-x']),
                                                    int(self.show_params['hint-y']),
                                                  text=self.show_params['hint-text'],
                                                  fill=self.show_params['hint-colour'],
                                                font=self.show_params['hint-font'],
                                                anchor=NW,
                                                tag='pp-content')

        self.canvas.tag_raise('pp-click-area')
        self.canvas.update_idletasks( )
        return 'normal',''


# ****************
# utilities
# *****************

    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Background image is "+ track_file)
        return track_file

# original _
# warp _ or xy2


    def parse_window(self,line):
        
            fields = line.split()
            # check there is a command field
            if len(fields) < 1:
                    return 'error','no type field','',False,0,0,0,0
                
            # deal with original which has 1
            if fields[0]=='original':
                if len(fields) <> 1:
                        return 'error','number of fields for original','',False,0,0,0,0    
                return 'normal','',fields[0],False,0,0,0,0


            #deal with warp which has 1 or 5  arguments
            # check basic syntax
            if  fields[0] <>'warp':
                    return 'error','not a valid type','',False,0,0,0,0
            if len(fields) not in (1,5):
                    return 'error','wrong number of coordinates for warp','',False,0,0,0,0

            # deal with window coordinates    
            if len(fields) == 5:
                #window is specified
                if not (fields[1].isdigit() and fields[2].isdigit() and fields[3].isdigit() and fields[4].isdigit()):
                    return 'error','coordinates are not positive integers','',False,0,0,0,0
                has_window=True
                return 'normal','',fields[0],has_window,int(fields[1]),int(fields[2]),int(fields[3]),int(fields[4])
            else:
                # fullscreen
                has_window=True
                return 'normal','',fields[0],has_window,0,0,self.canvas['width'],self.canvas['height']
Ejemplo n.º 10
0
    def __init__(self, show_id, root, canvas, show_params, track_params, pp_dir, pp_home, pp_profile):

        self.mon = Monitor()
        self.mon.off()

        # instantiate arguments
        self.show_id = show_id
        self.root = root
        self.canvas = canvas
        self.show_params = show_params
        self.track_params = track_params
        self.pp_dir = pp_dir
        self.pp_home = pp_home
        self.pp_profile = pp_profile

        # get duration limit (secs ) from profile
        if self.track_params["duration"] <> "":
            self.duration = int(self.track_params["duration"])
            self.duration_limit = 20 * self.duration
        else:
            self.duration_limit = -1

        # get background image from profile.
        self.background_file = ""
        if self.track_params["background-image"] <> "":
            self.background_file = self.track_params["background-image"]
        else:
            if self.track_params["display-show-background"] == "yes":
                self.background_file = self.show_params["background-image"]

        # get background colour from profile.
        if self.track_params["background-colour"] <> "":
            self.background_colour = self.track_params["background-colour"]
        else:
            self.background_colour = self.show_params["background-colour"]

        # get audio device from profile.
        if self.track_params["mplayer-audio"] <> "":
            self.mplayer_audio = self.track_params["mplayer-audio"]
        else:
            self.mplayer_audio = self.show_params["mplayer-audio"]

        # get audio volume from profile.
        if self.track_params["mplayer-volume"] <> "":
            self.mplayer_volume = self.track_params["mplayer-volume"].strip()
        else:
            self.mplayer_volume = self.show_params["mplayer-volume"].strip()
        self.volume_option = "volume=" + self.mplayer_volume

        # get speaker from profile
        if self.track_params["audio-speaker"] <> "":
            self.audio_speaker = self.track_params["audio-speaker"]
        else:
            self.audio_speaker = self.show_params["audio-speaker"]

        if self.audio_speaker == "left":
            self.speaker_option = AudioPlayer._LEFT
        elif self.audio_speaker == "right":
            self.speaker_option = AudioPlayer._RIGHT
        else:
            self.speaker_option = AudioPlayer._STEREO

        # get animation instructions from profile
        self.animate_begin_text = self.track_params["animate-begin"]
        self.animate_end_text = self.track_params["animate-end"]

        # open the plugin Manager
        self.pim = PluginManager(
            self.show_id,
            self.root,
            self.canvas,
            self.show_params,
            self.track_params,
            self.pp_dir,
            self.pp_home,
            self.pp_profile,
        )

        # create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # could put instance generation in play, not sure which is better.
        self.mplayer = mplayerDriver(self.canvas)
        self.tick_timer = None
        self.init_play_state_machine()
Ejemplo n.º 11
0
class VideoPlayer:

    _CLOSED = "omx_closed"    #probably will not exist
    _STARTING = "omx_starting"  #track is being prepared
    _PLAYING = "omx_playing"  #track is playing to the screen, may be paused
    _ENDING = "omx_ending"  #track is in the process of ending due to quit or end of track


# ***************************************
# EXTERNAL COMMANDS
# ***************************************

    def __init__(self,
                         show_id,
                        canvas,
                        pp_home,
                        show_params,
                        track_params ):
        """       
            canvas - the canvas onto which the video is to be drawn (not!!)
            cd - configuration dictionary
            track_params - config dictionary for this track overides cd
        """
        self.mon=Monitor()
        self.mon.on()
        
        #instantiate arguments
        self.show_id=show_id
        self.show_params=show_params       #configuration dictionary for the videoplayer
        self.pp_home=pp_home
        self.canvas = canvas  #canvas onto which video should be played but isn't! Use as widget for alarm
        self.track_params=track_params
        
        # get config from medialist if there.
        if self.track_params['omx-audio']<>"":
            self.omx_audio= self.track_params['omx-audio']
        else:
            self.omx_audio= self.show_params['omx-audio']
        if self.omx_audio<>"": self.omx_audio= "-o "+ self.omx_audio
        
        if self.track_params['omx-volume']<>"":
            self.omx_volume= self.track_params['omx-volume']
        else:
            self.omx_volume= self.show_params['omx-volume']
        if self.omx_volume<>"": self.omx_volume= "--vol "+ str(int(self.omx_volume)*100) + ' '
        self.omx_volume=' '
        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()        
        
        # could put instance generation in play, not sure which is better.
        self.omx=OMXDriver(self.canvas)
        self._tick_timer=None
        self.error=False
        self.terminate_required=False
        self._init_play_state_machine()



    def play(self, track,
                     end_callback,
                     ready_callback,
                     enable_menu=False, 
                     starting_callback=None,
                     playing_callback=None,
                     ending_callback=None):
                         
        #instantiate arguments
        self.ready_callback=ready_callback   #callback when ready to play
        self.end_callback=end_callback         # callback when finished
        self.starting_callback=starting_callback  #callback during starting state
        self.playing_callback=playing_callback    #callback during playing state
        self.ending_callback=ending_callback      # callback during ending state
        # enable_menu is not used by videoplayer
 

        # create animation events
        error_text=self.ppio.animate(self.animate_begin_text,id(self))
        if error_text<>'':
            self.mon.err(self,error_text)
            self.error=True
            self._end()
            
        # and start playing the video.
        if self.play_state == VideoPlayer._CLOSED:
            self.mon.log(self,">play track received")
            self._start_play_state_machine(track)
            return True
        else:
            self.mon.log(self,"!< play track rejected")
            return False


    def key_pressed(self,key_name):
        if key_name=='':
            return
        elif key_name in ('p',' '):
            self._pause()
            return
        elif key_name=='escape':
            self._stop()
            return



    def button_pressed(self,button,edge):
        if button =='pause':
            self._pause()
            return
        elif button=='stop':
            self._stop()
            return


    def terminate(self,reason):
        # circumvents state machine
        self.terminate_required=True
        if self.omx<>None:
            self.mon.log(self,"sent terminate to omxdriver")
            self.omx.terminate(reason)
        else:
            self.mon.log(self,"terminate, omxdriver not running")
            self._end()
            
                

        
# ***************************************
# INTERNAL FUNCTIONS
# ***************************************

    # respond to normal stop
    def _stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,">stop received")
        self._stop_required_signal=True

    #respond to internal error
    def _error(self):
        self.error=True
        self._stop_required_signal=True

    #toggle pause
    def _pause(self):
        if self.play_state in (VideoPlayer._PLAYING,VideoPlayer._ENDING):
            self.omx.pause()
            return True
        else:
            self.mon.log(self,"!<pause rejected")
            return False
        
    # other control when playing, used?
    def _control(self,char):
        if self.play_state==VideoPlayer._PLAYING:
            self.mon.log(self,"> send control ot omx: "+ char)
            self.omx.control(char)
            return True
        else:
            self.mon.log(self,"!<control rejected")
            return False

    # called to end omxdriver
    def _end(self):
            os.system("xrefresh -display :0")
            if self._tick_timer<>None:
                self.canvas.after_cancel(self._tick_timer)
                self._tick_timer=None
            if self.error==True:
                self.end_callback("error",'error')
                self=None 
            elif self.terminate_required==True:
                self.end_callback("killed",'killed')
                self=None
            else:
               # clear events list for this track
                if self.track_params['animate-clear']=='yes':
                    self.ppio.clear_events_list(id(self))
                # create animation events for ending
                error_text=self.ppio.animate(self.animate_end_text,id(self))
                if error_text=='':
                    self.end_callback('normal',"track has terminated or quit")
                    self=None
                else:
                    self.mon.err(self,error_text)
                    self.end_callback("error",'error')
                    self=None

# ***************************************
# # PLAYING STATE MACHINE
# ***************************************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the omx process is not running, omx process can be initiated
         - _starting - omx process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - omx is doing its termination, controls cannot be sent
    """

    def _init_play_state_machine(self):
        self._stop_required_signal=False
        self.play_state=VideoPlayer._CLOSED
 
    def _start_play_state_machine(self,track):
        #initialise all the state machine variables
        #self.iteration = 0                             # for debugging
        self._stop_required_signal=False     # signal that user has pressed stop
        self.play_state=VideoPlayer._STARTING
        
        #play the selected track
        options=self.omx_audio+ " " + self.omx_volume + ' ' + self.show_params['omx-other-options']+" "
        self.omx.play(track,options)
        self.mon.log (self,'Playing track from show Id: '+ str(self.show_id))
        # and start polling for state changes
        self._tick_timer=self.canvas.after(50, self._play_state_machine)
 

    def _play_state_machine(self):      
        if self.play_state == VideoPlayer._CLOSED:
            self.mon.log(self,"      State machine: " + self.play_state)
            return 
                
        elif self.play_state == VideoPlayer._STARTING:
            self.mon.log(self,"      State machine: " + self.play_state)
            
            # if omxplayer is playing the track change to play state
            if self.omx.start_play_signal==True:
                self.mon.log(self,"            <start play signal received from omx")
                self.omx.start_play_signal=False
                # callback to the calling object to e.g remove egg timer.
                if self.ready_callback<>None:
                    self.ready_callback()
                self.canvas.config(bg='black')
                self.canvas.delete(ALL)
                self.display_image()
                self.play_state=VideoPlayer._PLAYING
                self.mon.log(self,"      State machine: omx_playing started")
            self._do_starting()
            self._tick_timer=self.canvas.after(50, self._play_state_machine)

        elif self.play_state == VideoPlayer._PLAYING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # service any queued stop signals
            if self._stop_required_signal==True:
                self.mon.log(self,"      Service stop required signal")
                self._stop_omx()
                self._stop_required_signal=False
                self.play_state = VideoPlayer._ENDING
                
            # omxplayer reports it is terminating so change to ending state
            if self.omx.end_play_signal:                    
                self.mon.log(self,"            <end play signal received")
                self.mon.log(self,"            <end detected at: " + str(self.omx.video_position))
                self.play_state = VideoPlayer._ENDING
                
            self._do_playing()
            self._tick_timer=self.canvas.after(200, self._play_state_machine)

        elif self.play_state == VideoPlayer._ENDING:
            self.mon.log(self,"      State machine: " + self.play_state)
            self._do_ending()
            # if spawned process has closed can change to closed state
            # self.mon.log (self,"      State machine : is omx process running? -  "  + str(self.omx.is_running()))
            if self.omx.is_running() ==False:
                self.mon.log(self,"            <omx process is dead")
                self.play_state = VideoPlayer._CLOSED
                self._end()
            else:
                self._tick_timer=self.canvas.after(200, self._play_state_machine)


    # allow calling object do things in each state by calling the appropriate callback
 
    def _do_playing(self):
        self.video_position=self.omx.video_position
        self.audio_position=self.omx.audio_position
        if self.playing_callback<>None:
                self.playing_callback() 

    def _do_starting(self):
        self.video_position=0.0
        self.audio_position=0.0
        if self.starting_callback<>None:
                self.starting_callback() 

    def _do_ending(self):
        if self.ending_callback<>None:
                self.ending_callback() 

    def _stop_omx(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,"         >stop omx received from state machine")
        if self.play_state==VideoPlayer._PLAYING:
            self.omx.stop()
            return True
        else:
            self.mon.log(self,"!<stop rejected")
            return False
# *****************
# image and text
# *****************
            
    def display_image(self):

        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'])

        self.mon.log(self,"Displayed  text ")
        
        self.canvas.update_idletasks( )
Ejemplo n.º 12
0
class MessagePlayer:
    """ Displays lines of text in the centre of a coloured screen with background image
        See pp_imageplayer for common software design description
    """
    

# *******************
# external commands
# *******************

    def __init__(self,show_id,root,canvas,show_params,track_params,pp_dir,pp_home,pp_profile):

        self.mon=Monitor()
        self.mon.on()

        self.root=root
        self.canvas=canvas
        self.show_id=show_id
        self.track_params=track_params
        self.show_params=show_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile

       # get config from medialist if there.
       
        if 'duration' in self.track_params and self.track_params['duration']<>"":
            self.duration= int(self.track_params['duration'])
        else:
            self.duration= int(self.show_params['duration'])

        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>"":
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']
            
        self.centre_x = int(self.canvas['width'])/2
        self.centre_y = int(self.canvas['height'])/2
        
        # keep tick as an integer sub-multiple of 1 second         
        self.tick = 100 # tick time for image display (milliseconds)
        self.dwell = 1000*self.duration

        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO() 


    def play(self,
                    text,
                    showlist,
                    end_callback,
                    ready_callback,
                    enable_menu=False):
                        
        # instantiate arguments
        self.text=text
        self.showlist=showlist
        self.end_callback=end_callback
        self.ready_callback=ready_callback
        self.enable_menu=enable_menu
        
        #init state and signals
        self.quit_signal=False
        self.tick_timer=None
        self.drawn=None


        # create an  instance of showmanager so we can control concurrent shows
        self.show_manager=ShowManager(self.show_id,self.showlist,self.show_params,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home)

     # Control other shows at beginning
        reason,message=self.show_manager.show_control(self.track_params['show-control-begin'])
        if reason == 'error':
            self.end_callback(reason,message)
            self=None
        else:
            #display content
            reason,message=self.display_content()
            if reason == 'error':
                self.mon.err(self,message)
                self.end_callback(reason,message)
                self=None
            else:
                # create animation events
                reason,message=self.ppio.animate(self.animate_begin_text,id(self))
                if reason=='error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                    # start text display
                    self.start_dwell()

    def terminate(self,reason):
        # no lower level things to terminate so just go to end
        self.end(reason,'kill or error')

    def get_links(self):
        return self.track_params['links']

    def input_pressed(self,symbol):
        self.mon.log(self,"input received: "+symbol)
        if symbol=='stop':
            self.stop()



# *******************
# internal functions
# *******************

    def stop(self):
        self.quit_signal=True

        

            
# *******************
# sequencing
# *******************

    def start_dwell(self):
        self.dwell_counter=0
        if self.ready_callback<>None:
            self.ready_callback()
   
        self.tick_timer=self.canvas.after(self.tick, self.do_dwell)

        
    def do_dwell(self):
        if self.quit_signal == True:
            self.mon.log(self,"quit received")
            self.end('normal','user quit')
        else:
            if self.dwell<>0:
                self.dwell_counter=self.dwell_counter+1
                if self.dwell_counter==self.dwell/self.tick:
                    self.end('normal','finished')
                else:
                    self.tick_timer=self.canvas.after(self.tick, self.do_dwell)
            else:
                    self.tick_timer=self.canvas.after(self.tick, self.do_dwell)


# *****************
# ending the player
# *****************

    def end(self,reason,message):
        # stop the plugin
        if self.track_params['plugin']<>'':
            self.pim.stop_plugin()

        # abort the timer
        if self.tick_timer<>None:
            self.canvas.after_cancel(self.tick_timer)
            self.tick_timer=None
        
        if reason in ('error','killed'):
            self.end_callback(reason,message)
            self=None

        else:
            # normal end so do show control 
            # Control concurrent shows at end
            reason,message=self.show_manager.show_control(self.track_params['show-control-end'])
            if reason =='error':
                self.mon.err(self,message)
                self.end_callback(reason,message)
                self=None
            else:
                # clear events list for this track
                if self.track_params['animate-clear']=='yes':
                    self.ppio.clear_events_list(id(self))
                
                # create animation events for ending
                reason,message=self.ppio.animate(self.animate_end_text,id(self))
                if reason=='error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                    self.end_callback('normal',"track has terminated or quit")
                    self=None



# *****************
# displaying things
# *****************

    def display_content(self):

        if  self.background_colour<>'':   
            self.canvas.config(bg=self.background_colour)
        
        self.canvas.delete('pp-content')
      
        if self.background_file<>'':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self,"Message background file not found: "+ self.background_img_file)
                self.end('error',"Message background file not found")
            else:
                pil_background_img=PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                              int(self.canvas['height'])/2,
                                              image=self.background,
                                              anchor=CENTER,
                                              tag='pp-content')

         # display show text if enabled
        if self.show_params['show-text']<> ''and self.track_params['display-show-text']=='yes':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'],
                                                tag='pp-content')

        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'],
                                                tag='pp-content')

        # execute the plugin if required
        if self.track_params['plugin']<>'':
            reason,message,self.text = self.pim.do_plugin(self.text,self.track_params['plugin'])
            if reason <> 'normal':
                return reason,message

 
        # display message text
        if self.track_params['message-x']<>'':
             self.canvas.create_text(int(self.track_params['message-x']), int(self.track_params['message-y']),
                                                    text=self.text.rstrip('\n'),
                                                    fill=self.track_params['message-colour'],
                                                    font=self.track_params['message-font'],
                                                    justify=self.track_params['message-justify'],
                                                    anchor = 'nw',
                                                    tag='pp-content')
        else:
            self.canvas.create_text(int(self.canvas['width'])/2, int(self.canvas['height'])/2,
                                                    text=self.text.rstrip('\n'),
                                                    fill=self.track_params['message-colour'],
                                                    font=self.track_params['message-font'],
                                                    justify=self.track_params['message-justify'],
                                                    tag='pp-content')     


        # display instructions (hint)
        if self.enable_menu==True:
            self.canvas.create_text(int(self.show_params['hint-x']),
                                            int(self.show_params['hint-y']),
                                            text=self.show_params['hint-text'],
                                            fill=self.show_params['hint-colour'],
                                            font=self.show_params['hint-font'],
                                            anchor=NW,
                                           tag='pp-content')
            
        self.canvas.tag_raise('pp-click-area')
        self.canvas.update_idletasks( )
        return 'normal',''

# *****************
# utilities
# *****************


    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Background image is "+ track_file)
        return track_file     
Ejemplo n.º 13
0
    def __init__(self,show_id,root,canvas,show_params,track_params,pp_dir,pp_home,pp_profile):
        """
                show_id - show instance that player is run from (for monitoring only)
                canvas - the canvas onto which the image is to be drawn
                show_params -  dictionary of show parameters
                track_params - disctionary of track paramters
                pp_home - data home directory
                pp_profile - profile name
        """

        self.mon=Monitor()
        self.mon.off()

        self.show_id=show_id
        self.root=root
        self.canvas=canvas
        self.show_params=show_params
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile


        # open resources
        self.rr=ResourceReader()

        # get parameters 
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']
        
        if self.track_params['duration']<>"":
            self.duration= int(self.track_params['duration'])
        else:
            self.duration= int(self.show_params['duration'])
        
        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>'':
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']


        # get  image window from profile
        if self.track_params['image-window'].strip()<>"":
            self.image_window= self.track_params['image-window'].strip()
        else:
            self.image_window= self.show_params['image-window'].strip()

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 
Ejemplo n.º 14
0
class ImagePlayer:
    """ Displays an image on a canvas for a period of time. Image display can be paused and interrupted
        __init_ just makes sure that all the things the player needs are available
        play starts playing the track and returns immeadiately
        play must end up with a call to tkinter's after, the after callback will interrogae the playing state at intervals and
        eventually return through end_callback
        input-pressed receives user input while the track is playing. it might pass the input on to the driver
        Input-pressed must not wait, it must set a signal and return immeadiately.
        The signal is interrogated by the after callback.
    """

    # slide state constants
    NO_SLIDE = 0
    SLIDE_DWELL= 1

# *******************
# external commands
# *******************

    def __init__(self,show_id,root,canvas,show_params,track_params,pp_dir,pp_home,pp_profile):
        """
                show_id - show instance that player is run from (for monitoring only)
                canvas - the canvas onto which the image is to be drawn
                show_params -  dictionary of show parameters
                track_params - disctionary of track paramters
                pp_home - data home directory
                pp_profile - profile name
        """

        self.mon=Monitor()
        self.mon.off()

        self.show_id=show_id
        self.root=root
        self.canvas=canvas
        self.show_params=show_params
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile


        # open resources
        self.rr=ResourceReader()

        # get parameters 
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']
        
        if self.track_params['duration']<>"":
            self.duration= int(self.track_params['duration'])
        else:
            self.duration= int(self.show_params['duration'])
        
        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>'':
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']


        # get  image window from profile
        if self.track_params['image-window'].strip()<>"":
            self.image_window= self.track_params['image-window'].strip()
        else:
            self.image_window= self.show_params['image-window'].strip()

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

    def play(self,
                    track,
                    showlist,
                    end_callback,
                    ready_callback,
                    enable_menu=False):

        """
                track - filename of track to be played
                showlist - from which track was taken
                end_callback - callback when player terminates
                ready_callback - callback just before anytthing is displayed
                enable_menu  - there will be a child track so display the hint text
        """

        # instantiate arguments
        self.track=track
        self.showlist=showlist
        self.enable_menu=enable_menu
        self.ready_callback=ready_callback
        self.end_callback=end_callback

        #init state and signals  
        self.canvas_centre_x = int(self.canvas['width'])/2
        self.canvas_centre_y = int(self.canvas['height'])/2
        self.tick = 100 # tick time for image display (milliseconds)
        self.dwell = 10*self.duration
        self.dwell_counter=0
        self.state=ImagePlayer.NO_SLIDE
        self.quit_signal=False
        self.drawn=None
        self.paused=False
        self.pause_text=None
        self.tick_timer=None
        

        #parse the image_window
        error,self.command,self.has_coords,self.image_x1,self.image_y1,self.image_x2,self.image_y2,self.filter=self.parse_window(self.image_window)
        if error =='error':
            self.mon.err(self,'image window error: '+self.image_window)
            self.end('error','image window error')
        else:


            # create an  instance of showmanager so we can control concurrent shows
            self.show_manager=ShowManager(self.show_id,self.showlist,self.show_params,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home)

             # Control other shows at beginning
            reason,message=self.show_manager.show_control(self.track_params['show-control-begin'])
            if reason == 'error':
                self.end_callback(reason,message)
                self=None
            else:
                #display content
                reason,message=self.display_content()
                if reason == 'error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                    # create animation events
                    reason,message=self.ppio.animate(self.animate_begin_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:                              
                        # start dwelling
                        self.mon.log(self,'playing track from show Id: '+str(self.show_id))
                        self.start_dwell()

        
    def input_pressed(self,symbol):
        if symbol =='pause':
            self.pause()
        elif symbol=='stop':
            self.stop()
            return

    def terminate(self,reason):
        # no lower level things to terminate so just go to end
        self.end(reason,'kill or error')

    def get_links(self):
        return self.track_params['links']
      
# *******************
# internal functions
# *******************

    def pause(self):
        if not self.paused:
            self.paused = True
        else:
            self.paused=False

        #print "self.paused is "+str(self.paused)

    def stop(self):
        self.quit_signal=True
        


        
# ******************************************
# Sequencing
# ********************************************

    def start_dwell(self):

        if self.ready_callback<>None:
            self.ready_callback()
        self.state=ImagePlayer.SLIDE_DWELL
        self.tick_timer=self.canvas.after(self.tick, self.do_dwell)

        
    def do_dwell(self):
        if self.quit_signal == True:
            self.mon.log(self,"quit received")
            self.end('normal','user quit')
        else:
            if self.paused == False:
                self.dwell_counter=self.dwell_counter+1

            #print "self.paused is "+str(self.paused)
            #if self.pause_text<>None:
                #print "self.pause_text exists"

            # one time flipping of pause text
            #NIK
            if self.paused==True and self.pause_text==None:
                self.pause_text=self.canvas.create_text(0,900, anchor=NW,
                                                      text=self.resource('imageplayer','m01'),
                                                      fill="white",
                                                      font="arial 25 bold")
                self.canvas.update_idletasks( )
            #NIK

            if self.paused==False and self.pause_text<>None:
                self.canvas.delete(self.pause_text)
                self.pause_text=None
                self.canvas.update_idletasks( )

            if self.dwell<>0 and self.dwell_counter==self.dwell:
                self.end('normal','user quit or duration exceeded')
            else:
                self.tick_timer=self.canvas.after(self.tick, self.do_dwell)



# *****************
# ending the player
# *****************

    def end(self,reason,message):
            self.state=self.NO_SLIDE

            # stop the plugin
            if self.track_params['plugin']<>'':
                self.pim.stop_plugin()
                             
            # abort the timer
            if self.tick_timer<>None:
                self.canvas.after_cancel(self.tick_timer)
                self.tick_timer=None
            
            if reason in ('error','killed'):
                self.end_callback(reason,message)
                self=None

            else:
                # normal end so do show control and animation

                # Control concurrent shows at end
                reason,message=self.show_manager.show_control(self.track_params['show-control-end'])
                if reason =='error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                   # clear events list for this track
                    if self.track_params['animate-clear']=='yes':
                        self.ppio.clear_events_list(id(self))

                    # create animation events for ending
                    reason,message=self.ppio.animate(self.animate_end_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        self.end_callback('normal',"track has terminated or quit")
                        #NIK
                        if self.pause_text<>None:
                            self.canvas.delete(self.pause_text)
                            self.pause_text=None
                            self.canvas.update_idletasks( )
                        #NIK
                        self=None



# **********************************
# displaying things
# **********************************

    def display_content(self):

        #background colour
        if  self.background_colour<>'':   
           self.canvas.config(bg=self.background_colour)
           
        self.canvas.delete('pp-content')


        # background image
        if self.background_file<> '':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self,"Background file not found: "+ self.background_img_file)
                self.end('error',"Background file not found")
            else:
                pil_background_img=PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                             int(self.canvas['height'])/2,
                                             image=self.background,
                                            anchor=CENTER,
                                            tag='pp-content')

        # execute the plugin if required
        if self.track_params['plugin']<>'':

            reason,message,self.track = self.pim.do_plugin(self.track,self.track_params['plugin'],)
            if reason <> 'normal':
                return reason,message

        #get the track to be displayed
        if os.path.exists(self.track)==True:
            self.pil_image=PIL.Image.open(self.track)
        else:
            self.pil_image=None
            
        # display track image                                    
        if self.pil_image<>None:
            self.image_width,self.image_height=self.pil_image.size

            if self.command=='original':
                # display image at its original size
                if self.has_coords==False:
                    # load and display the unmodified image in centre
                    self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                    self.drawn = self.canvas.create_image(self.canvas_centre_x, self.canvas_centre_y,
                                                  image=self.tk_img, anchor=CENTER,
                                                  tag='pp-content')
                else:
                    # load and display the unmodified image at x1,y1
                    self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                    self.drawn = self.canvas.create_image(self.image_x1, self.image_y1,
                                                      image=self.tk_img, anchor=NW,
                                                      tag='pp-content')


            elif self.command in ('fit','shrink'):
                    # shrink fit the window or screen preserving aspect
                    if self.has_coords==True:
                        window_width=self.image_x2 - self.image_x1
                        window_height=self.image_y2 - self.image_y1
                        window_centre_x=(self.image_x2+self.image_x1)/2
                        window_centre_y= (self.image_y2+self.image_y1)/2
                    else:
                        window_width=int(self.canvas['width'])
                        window_height=int(self.canvas['height'])
                        window_centre_x=self.canvas_centre_x
                        window_centre_y=self.canvas_centre_y
                    
                    if (self.image_width > window_width or self.image_height > window_height and self.command=='fit') or (self.command=='shrink') :
                        # original image is larger or , shrink it to fit the screen preserving aspect
                        self.pil_image.thumbnail((window_width,window_height),eval(self.filter))                 
                        self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                        self.drawn = self.canvas.create_image(window_centre_x, window_centre_y,
                                                      image=self.tk_img, anchor=CENTER,
                                                      tag='pp-content')
                    else:
                        # fitting and original image is smaller, expand it to fit the screen preserving aspect
                        prop_x = float(window_width) / self.image_width
                        prop_y = float(window_height) / self.image_height
                        if prop_x > prop_y:
                            prop=prop_y
                        else:
                            prop=prop_x
                            
                        increased_width=int(self.image_width * prop)
                        increased_height=int(self.image_height * prop)
                        # print 'result',prop, increased_width,increased_height
                        self.pil_image=self.pil_image.resize((increased_width, increased_height),eval(self.filter))
                        self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                        self.drawn = self.canvas.create_image(window_centre_x, window_centre_y,
                                                      image=self.tk_img, anchor=CENTER,
                                                      tag='pp-content')                                                 
                                                  
            elif self.command in ('warp'):
                    # resize to window or screen without preserving aspect
                    if self.has_coords==True:
                        window_width=self.image_x2 - self.image_x1
                        window_height=self.image_y2 - self.image_y1
                        window_centre_x=(self.image_x2+self.image_x1)/2
                        window_centre_y= (self.image_y2+self.image_y1)/2
                    else:
                        window_width=int(self.canvas['width'])
                        window_height=int(self.canvas['height'])
                        window_centre_x=self.canvas_centre_x
                        window_centre_y=self.canvas_centre_y
                    
                    self.pil_image=self.pil_image.resize((window_width, window_height),eval(self.filter))
                    self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                    self.drawn = self.canvas.create_image(window_centre_x, window_centre_y,
                                                  image=self.tk_img, anchor=CENTER,
                                                  tag='pp-content')                                                        
                                              
        # display hint if enabled
       
        if self.enable_menu== True:
            self.canvas.create_text(int(self.show_params['hint-x']),
                                                    int(self.show_params['hint-y']),
                                                  text=self.show_params['hint-text'],
                                                  fill=self.show_params['hint-colour'],
                                                font=self.show_params['hint-font'],
                                                anchor=NW,
                                                tag='pp-content')

        # display show text if enabled
        if self.show_params['show-text']<> ''and self.track_params['display-show-text']=='yes':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'],
                                                    tag='pp-content')
            
        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'],
                                                tag='pp-content')
            
        self.canvas.tag_raise('pp-click-area')            
        self.canvas.update_idletasks( )
        return 'normal',''

# **********************************
# utilties
# **********************************


    def parse_window(self,line):
        
            fields = line.split()
            # check there is a command field
            if len(fields) < 1:
                    return 'error','',False,0,0,0,0,''
                
            # deal with original whch has 0 or 2 arguments
            filter=''
            if fields[0]=='original':
                if len(fields) not in (1,3):
                        return 'error','',False,0,0,0,0,''       
                # deal with window coordinates    
                if len(fields) == 3:
                    #window is specified
                    if not (fields[1].isdigit() and fields[2].isdigit()):
                        return 'error','',False,0,0,0,0,''
                    has_window=True
                    return 'normal',fields[0],has_window,int(fields[1]),int(fields[2]),0,0,filter
                else:
                    # no window
                    has_window=False 
                    return 'normal',fields[0],has_window,0,0,0,0,filter



            #deal with remainder which has 1, 2, 5 or  6arguments
            # check basic syntax
            if  fields[0] not in ('shrink','fit','warp'):
                    return 'error','',False,0,0,0,0,'' 
            if len(fields) not in (1,2,5,6):
                    return 'error','',False,0,0,0,0,''
            if len(fields)==6 and fields[5] not in ('NEAREST','BILINEAR','BICUBIC','ANTIALIAS'):
                    return 'error','',False,0,0,0,0,''
            if len(fields)==2 and fields[1] not in ('NEAREST','BILINEAR','BICUBIC','ANTIALIAS'):
                    return 'error','',False,0,0,0,0,''
            
            # deal with window coordinates    
            if len(fields) in (5,6):
                #window is specified
                if not (fields[1].isdigit() and fields[2].isdigit() and fields[3].isdigit() and fields[4].isdigit()):
                    return 'error','',False,0,0,0,0,''
                has_window=True
                if len(fields)==6:
                    filter=fields[5]
                else:
                    filter='PIL.Image.NEAREST'
                    return 'normal',fields[0],has_window,int(fields[1]),int(fields[2]),int(fields[3]),int(fields[4]),filter
            else:
                # no window
                has_window=False
                if len(fields)==2:
                    filter=fields[1]
                else:
                    filter='PIL.Image.NEAREST'
                return 'normal',fields[0],has_window,0,0,0,0,filter
                


                    
    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Background image is "+ track_file)
        return track_file     
 
  

# get a text string from resources.cfg
    def resource(self,section,item):
        value=self.rr.get(section,item)
        if value==False:
            self.mon.err(self, "resource: "+section +': '+ item + " not found" )
            self.error()
        else:
            return value
Ejemplo n.º 15
0
    def __init__(self, show_id, root, canvas, show_params, track_params,
                 pp_dir, pp_home, pp_profile):

        self.mon = Monitor()
        self.mon.off()

        #instantiate arguments
        self.show_id = show_id
        self.root = root
        self.canvas = canvas
        self.show_params = show_params
        self.track_params = track_params
        self.pp_dir = pp_dir
        self.pp_home = pp_home
        self.pp_profile = pp_profile

        # get duration limit (secs ) from profile
        if self.track_params['duration'] <> '':
            self.duration = int(self.track_params['duration'])
            self.duration_limit = 20 * self.duration
        else:
            self.duration_limit = -1

        # get background image from profile.
        self.background_file = ''
        if self.track_params['background-image'] <> "":
            self.background_file = self.track_params['background-image']
        else:
            if self.track_params['display-show-background'] == 'yes':
                self.background_file = self.show_params['background-image']

        # get background colour from profile.
        if self.track_params['background-colour'] <> "":
            self.background_colour = self.track_params['background-colour']
        else:
            self.background_colour = self.show_params['background-colour']

        # get audio device from profile.
        if self.track_params['mplayer-audio'] <> "":
            self.mplayer_audio = self.track_params['mplayer-audio']
        else:
            self.mplayer_audio = self.show_params['mplayer-audio']

        # get audio volume from profile.
        if self.track_params['mplayer-volume'] <> "":
            self.mplayer_volume = self.track_params['mplayer-volume'].strip()
        else:
            self.mplayer_volume = self.show_params['mplayer-volume'].strip()
        self.volume_option = 'volume=' + self.mplayer_volume

        #get speaker from profile
        if self.track_params['audio-speaker'] <> "":
            self.audio_speaker = self.track_params['audio-speaker']
        else:
            self.audio_speaker = self.show_params['audio-speaker']

        if self.audio_speaker == 'left':
            self.speaker_option = AudioPlayer._LEFT
        elif self.audio_speaker == 'right':
            self.speaker_option = AudioPlayer._RIGHT
        else:
            self.speaker_option = AudioPlayer._STEREO

        #get animation instructions from profile
        self.animate_begin_text = self.track_params['animate-begin']
        self.animate_end_text = self.track_params['animate-end']

        # open the plugin Manager
        self.pim = PluginManager(self.show_id, self.root, self.canvas,
                                 self.show_params, self.track_params,
                                 self.pp_dir, self.pp_home, self.pp_profile)

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # could put instance generation in play, not sure which is better.
        self.mplayer = mplayerDriver(self.canvas)
        self.tick_timer = None
        self.init_play_state_machine()
Ejemplo n.º 16
0
class Test:
    def __init__(self, track, pp_dir, pp_home, show_params, ct):

        self.track = track
        self.show_params = show_params
        self.pp_home = pp_home
        self.ct = ct
        self.break_from_loop = False

        # create and instance of a Tkinter top level window and refer to it as 'my_window'
        my_window = Tk()
        my_window.title("AudioPlayer Test Harness")

        # change the look of the window
        my_window.configure(background='grey')
        window_width = 1500
        window_height = 900

        canvas_height = window_height
        canvas_width = window_width

        #defne response to main window closing
        my_window.protocol("WM_DELETE_WINDOW", self.terminate)

        my_window.geometry("%dx%d+200+20" % (window_width, window_height))

        # Always use CTRL-Break key to close the program as a get out of jail
        my_window.bind("<Break>", self.e_terminate)

        my_window.bind("s", self.play_event)
        my_window.bind("p", self.pause_event)
        my_window.bind("q", self.stop_event)
        my_window.bind("l", self.loop_event)
        my_window.bind("n", self.next_event)

        #setup a canvas onto which will not be drawn the video!!
        canvas = Canvas(my_window, bg='black')
        canvas.config(height=canvas_height, width=canvas_width)
        canvas.pack()
        # make sure focus is set on canvas.
        canvas.focus_set()
        self.canvas = canvas

        #create an instance of PPIO and start polling
        self.ppio = PPIO()
        self.ppio.init(pp_dir, pp_home, self.canvas, 50, self.callback)
        self.ppio.poll()

        my_window.mainloop()

    def callback(self, index, name, edge):
        print name, edge

    #key presses

    def e_terminate(self, event):
        self.terminate()

    def play_event(self, event):
        self.vp = AudioPlayer(self.canvas, self.pp_home, self.show_params,
                              self.ct)
        self.vp.play(self.track, self.on_end, self.do_ready, False,
                     self.do_starting, self.do_playing, self.do_finishing)

    # toggles pause
    def pause_event(self, event):
        self.vp.key_pressed('p')

    def stop_event(self, event):
        self.break_from_loop = True
        self.vp.key_pressed('escape')

    def loop_event(self, event):
        #just kick off the first track, callback decides what to do next
        self.break_from_loop = False
        self.vp = AudioPlayer(self.canvas, self.pp_home, self.show_params)
        self.vp.play(self.track, self.what_next, self, do_ready, False,
                     self.do_starting, self.do_playing, self.do_finishing)

    def next_event(self, event):
        self.break_from_loop = False
        self.vp.key_pressed('down')

    def what_next(self, reason, message):
        self.vp = None
        if reason in ('killed', 'error'):
            self.end(reason, message)
        else:
            if self.break_from_loop == True:
                self.break_from_loop = False
                print "test harness: loop interupted"
                return
            else:
                self.vp = AudioPlayer(self.canvas, self.show_params)
                self.vp.play(self.track, self.what_next, self.do_starting,
                             self.do_playing, self.do_finishing)

    def on_end(self, reason, message):
        self.vp = None
        print "Test Class: callback from AudioPlayer says: " + message
        if reason in ('killed', 'error'):
            self.end(reason, message)
        else:
            return

    def do_ready(self):
        print "test class message from AudioPlayer: ready to play"
        return

    def do_starting(self):
        print "test class message from AudioPlayer: do starting"
        return

    def do_playing(self):
        #self.display_time.set(self.time_string(self.vp.audio_position))
        # print "test class message from AudioPlayer: do playing"
        return

    def do_finishing(self):
        print "test class message from AudioPlayer: do ending"
        return

    def terminate(self):
        if self.vp == None:
            self.end('killled', 'killled')
        else:
            self.vp.terminate('killed')
            return

    def _end(self, reason, message):
        self.ppio.terminate()
        exit()
Ejemplo n.º 17
0
class AudioPlayer:

    _CLOSED = "mplayer_closed"    #probably will not exist
    _STARTING = "mplayer_starting"  #track is being prepared
    _PLAYING = "mplayer_playing"  #track is playing to the screen, may be paused
    _ENDING = "mplayer_ending"  #track is in the process of ending due to quit or end of track
    _WAITING = "wait for timeout" # track has finished but timeout still running

    #_LEFT = "-af channels=2:1:0:0:1:1,resample=48000:1 "
   # _RIGHT = "-af channels=2:1:0:1:1:0,resample=48000:1 "
    #_STEREO = "-af channels=2,resample=48000:1 "

    _LEFT = "channels=2:1:0:0:1:1"
    _RIGHT = "channels=2:1:0:1:1:0"
    _STEREO = "channels=2"

# ***************************************
# EXTERNAL COMMANDS
# ***************************************

    def __init__(self,
                        show_id,
                        canvas,
                         pp_home,
                        show_params,
                        track_params ):
        """       
            canvas - the canvas onto which the background image is to be drawn
            show_params - configuration of show playing the track
            track_params - config dictionary for this track overrides show_params
        """

        self.mon=Monitor()
        self.mon.on()

        
        #instantiate arguments
        self.show_id=show_id
        self.show_params=show_params       #configuration dictionary for the videoplayer
        self.canvas = canvas  #canvas onto which video should be played but isn't! Use as widget for alarm
        self.pp_home=pp_home
        self.track_params=track_params

        # get duration (secs ) from profile
        self.duration= int(self.track_params['duration'])
        self.duration_limit=20*self.duration


        # get background image from profile.
        self.background_file  = self.track_params['background-image']


        # get audio sink from profile.
        if  self.track_params['mplayer-audio']<>"":
            self.mplayer_audio= self.track_params['mplayer-audio']
        else:
            self.mplayer_audio= self.show_params['mplayer-audio']
            
        # get audio volume from profile.
        if  self.track_params['mplayer-volume']<>"":
            self.mplayer_volume= self.track_params['mplayer-volume'].strip()
        else:
            self.mplayer_volume= self.show_params['mplayer-volume'].strip()
        self.volume_option= 'volume=' + self.mplayer_volume


        #get speaker from profile
        if  self.track_params['audio-speaker']<>"":
            self.audio_speaker= self.track_params['audio-speaker']
        else:
            self.audio_speaker= self.show_params['audio-speaker']

        if self.audio_speaker=='left':
            self.speaker_option=AudioPlayer._LEFT
        elif self.audio_speaker=='right':
            self.speaker_option=AudioPlayer._RIGHT
        else:
            self.speaker_option=AudioPlayer._STEREO

        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']


        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # could put instance generation in play, not sure which is better.
        self.mplayer=mplayerDriver(self.canvas)
        self._tick_timer=None
        self.error=False
        self.terminate_me=False
        self._init_play_state_machine()



    def play(self, track,
                     end_callback,
                     ready_callback,
                     enable_menu=False, 
                     starting_callback=None,
                     playing_callback=None,
                     ending_callback=None):

        """
        play - plays the specified track, the first call after __init__
        track - full  path of track to play
        end_callback - callback when track ends (reason,message)
             reason = killed - return from a terminate with reason = killed
                           error - return because player or lower level has generated and runtime error
                           normal - anything else
            message = ant tesxt, used for debugging 
        ready_callback - callback when the track is ready to play, use to stop eggtimer etc.
        enable_menu - True if the track is to have a child show
        starting/playing/ending callback - called repeatedly in each state for show to display status, time etc.

        """
                         
        #instantiate arguments
        self.track=track
        self.ready_callback=ready_callback   #callback when ready to play
        self.enable_menu=enable_menu
        self.end_callback=end_callback         # callback when finished
        self.starting_callback=starting_callback  #callback during starting state
        self.playing_callback=playing_callback    #callback during playing state
        self.ending_callback=ending_callback      # callback during ending state
        # enable_menu is not used by AudioPlayer


        # select the sound sink
        if self.mplayer_audio<>"":
            if self.mplayer_audio=='hdmi':
                os.system("amixer -q -c 0 cset numid=3 2")
            else:
                os.system("amixer -q -c 0 cset numid=3 1")

        # callback to the calling object to e.g remove egg timer.
        if self.ready_callback<>None:
            self.ready_callback()

 
        # display image and text
        self.display_image()

        # create animation events
        error_text=self.ppio.animate(self.animate_begin_text,id(self))
        if error_text<>'':
            self.mon.err(self,error_text)
            self.error=True
            self._end('error',error_text)
 
        # and start playing the track.
        if self.play_state == AudioPlayer._CLOSED:
            self.mon.log(self,">play track received")
            self._start_play_state_machine()
            return True
        else:
            self.mon.log(self,"!< play track rejected")
            return False


    def key_pressed(self,key_name):
        """
        respond to user or system key  presses
        """
        if key_name=='':
            return
        elif key_name in ('p',' '):
            self._pause()
            return
        elif key_name=='escape':
            self._stop()
            return



    def button_pressed(self,button,edge):
        """
        respond to user button  presses
        """
        if button =='pause':
            self._pause()
            return
        elif button=='stop':
            self._stop()
            return


    def terminate(self,reason):
        """
        terminate the  player in special circumstances
        normal user termination if by key_pressed 'escape'
        reason will be killed or error
        """
        # circumvents state machine to terminate lower level and then itself.
        self.terminate_me=True
        if self.mplayer<>None:
            self.mon.log(self,"sent terminate to mplayerdriver")
            self.mplayer.terminate(reason)
            self._end('killed',' end without waiting') # end without waiting
        else:
            self.mon.log(self,"terminate, mplayerdriver not running")
            self._end('killed','terminate, mplayerdriver not running')
            
                

        
# ***************************************
# INTERNAL FUNCTIONS
# ***************************************

    #toggle pause
    def _pause(self):
        if self.play_state in (AudioPlayer._PLAYING,AudioPlayer._ENDING) and self.track<>'':
            self.mplayer.pause()
            return True
        else:
            self.mon.log(self,"!<pause rejected")
            return False
        
    # other control when playing, not currently used
    def _control(self,char):
        if self.play_state==AudioPlayer._PLAYING and self.track<>'':
            self.mon.log(self,"> send control to mplayer: "+ char)
            self.mplayer.control(char)
            return True
        else:
            self.mon.log(self,"!<control rejected")
            return False

    # respond to normal stop
    def _stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,">stop received")
        self._stop_required_signal=True

    #respond to internal error by setting flags to cause state machine to stop player
    #use this rather than end if the driver and its spawned process might still be running
    def _error(self):
        self.error=True
        self._stop_required_signal=True


    # tidy up and end AudioPlayer.
    def _end(self,reason,message):
            # self.canvas.delete(ALL)
            # abort the timer
            if self._tick_timer<>None:
                self.canvas.after_cancel(self._tick_timer)
                self._tick_timer=None
            
            if self.error==True or reason=='error':
                self.end_callback("error",message)
                self=None
                
            elif self.terminate_me==True:
                self.end_callback("killed",message)
                self=None
            else:
               # clear events list for this track
                if self.track_params['animate-clear']=='yes':
                    self.ppio.clear_events_list(id(self))
                # create animation events for ending
                error_text=self.ppio.animate(self.animate_end_text,id(self))
                if error_text=='':
                    self.end_callback('normal',"track has terminated or quit")
                    self=None
                else:
                    self.mon.err(self,error_text)
                    self.end_callback("error",error_text)
                    self=None
                
      
# ***************************************
# # PLAYING STATE MACHINE
# ***************************************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the mplayer process is not running, mplayer process can be initiated
         - _starting - mplayer process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - mplayer is doing its termination, controls cannot be sent
    """

    def _init_play_state_machine(self):
        self._stop_required_signal=False
        self.play_state=AudioPlayer._CLOSED
 
    def _start_play_state_machine(self):
        #initialise all the state machine variables
        self.duration_count = 0
        self._stop_required_signal=False     # signal that user has pressed stop

        #play the track

        options = self.show_params['mplayer-other-options'] + '-af '+ self.speaker_option+','+self.volume_option + ' '
        if self.track<>'':
            self.mplayer.play(self.track,options)
            self.mon.log (self,'Playing track from show Id: '+ str(self.show_id))
            self.play_state=AudioPlayer._STARTING
        else:
            self.play_state=AudioPlayer._PLAYING
        # and start polling for state changes and count duration
        self._tick_timer=self.canvas.after(50, self._play_state_machine)
 

    def _play_state_machine(self):
        self.duration_count+=1
           
        if self.play_state == AudioPlayer._CLOSED:
            self.mon.log(self,"      State machine: " + self.play_state)
            return 
                
        elif self.play_state == AudioPlayer._STARTING:
            self.mon.log(self,"      State machine: " + self.play_state)
            
            # if mplayer is playing the track change to play state
            if self.mplayer.start_play_signal==True:
                self.mon.log(self,"            <start play signal received from mplayer")
                self.mplayer.start_play_signal=False
                self.play_state=AudioPlayer._PLAYING
                self.mon.log(self,"      State machine: mplayer_playing started")
            self._do_starting()
            self._tick_timer=self.canvas.after(50, self._play_state_machine)

        elif self.play_state == AudioPlayer._PLAYING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # service any queued stop signals
            if self._stop_required_signal==True or (self.duration_limit<>0 and self.duration_count>self.duration_limit):
                self.mon.log(self,"      Service stop required signa or timeout")
                # self._stop_required_signal=False
                if self.track<>'':
                    self._stop_mplayer()
                    self.play_state = AudioPlayer._ENDING
                else:
                    self.play_state = AudioPlayer._CLOSED
                    self._end('normal','stop required signa or timeout')

            # mplayer reports it is terminating so change to ending state
            if self.track<>'' and self.mplayer.end_play_signal:                    
                self.mon.log(self,"            <end play signal received")
                self.mon.log(self,"            <end detected at: " + str(self.mplayer.audio_position))
                self.play_state = AudioPlayer._ENDING
            self._do_playing()
            self._tick_timer=self.canvas.after(50, self._play_state_machine)

        elif self.play_state == AudioPlayer._ENDING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            self._do_ending()
            # if spawned process has closed can change to closed state
            # self.mon.log (self,"      State machine : is mplayer process running? -  "  + str(self.mplayer.is_running()))
            if self.mplayer.is_running() ==False:
                self.mon.log(self,"            <mplayer process is dead")
                if self._stop_required_signal==True:
                    self._stop_required_signal=False
                    self.play_state = AudioPlayer._CLOSED
                    self._end('normal','mplayer dead')
                elif self.duration_limit<>0 and self.duration_count<self.duration_limit:
                    self.play_state= AudioPlayer._WAITING
                    self._tick_timer=self.canvas.after(50, self._play_state_machine)
                else:
                    self.play_state = AudioPlayer._CLOSED
                    self._end('normal','mplayer dead')
            else:
                self._tick_timer=self.canvas.after(50, self._play_state_machine)
                
        elif self.play_state == AudioPlayer._WAITING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            if self._stop_required_signal==True or (self.duration_limit<>0 and self.duration_count>self.duration_limit):
                self.mon.log(self,"      Service stop required signal or timeout from wait")
                self._stop_required_signal=False
                self.play_state = AudioPlayer._CLOSED
                self._end('normal','mplayer dead')
            else:
                self._tick_timer=self.canvas.after(50, self._play_state_machine)
                    



    # allow calling object do things in each state by calling the appropriate callback
 
    def _do_playing(self):
        if self.track<>'':
            self.audio_position=self.mplayer.audio_position
        if self.playing_callback<>None:
                self.playing_callback() 

    def _do_starting(self):
        self.audio_position=0.0
        if self.starting_callback<>None:
                self.starting_callback() 

    def _do_ending(self):
        if self.ending_callback<>None:
                self.ending_callback() 

    def _stop_mplayer(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,"         >stop mplayer received from state machine")
        if self.play_state==AudioPlayer._PLAYING:
            self.mplayer.stop()
            return True
        else:
            self.mon.log(self,"!<stop rejected")
            return False

# *****************
# image and text
# *****************
            
    def display_image(self):

        if self.background_file<>'' or self.show_params['show-text']<> '' or self.track_params['track-text']<> '' or self.enable_menu== True or self.track_params['clear-screen']=='yes':
            self.canvas.config(bg='black')
            self.canvas.delete(ALL)
        if self.background_file<>'':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self,"Audio background file not found: "+ self.background_img_file)
                self._end('error',"Audio background file not found")
            else:
                pil_background_img=PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                              int(self.canvas['height'])/2,
                                              image=self.background,
                                              anchor=CENTER)
                
        # display hint text if enabled
       
        if self.enable_menu== True:
            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'])

            
        # display show text if enabled
        if self.show_params['show-text']<> '':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'])
            
        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'])

        self.mon.log(self,"Displayed background and text ")
        
        self.canvas.update_idletasks( )

    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Background image is "+ track_file)
        return track_file     
Ejemplo n.º 18
0
class AudioPlayer:
    """       
            plays an audio track using mplayer against a coloured backgroud and image
            track can be paused and interrupted
            See pp_imageplayer for common software design description
    """

    # state constants
    _CLOSED = "mplayer_closed"  # probably will not exist
    _STARTING = "mplayer_starting"  # track is being prepared
    _PLAYING = "mplayer_playing"  # track is playing to the screen, may be paused
    _ENDING = "mplayer_ending"  # track is in the process of ending due to quit or end of track
    _WAITING = "wait for timeout"  # track has finished but timeout still running

    # audio mixer matrix settings
    _LEFT = "channels=2:1:0:0:1:1"
    _RIGHT = "channels=2:1:0:1:1:0"
    _STEREO = "channels=2"

    # ***************************************
    # EXTERNAL COMMANDS
    # ***************************************

    def __init__(self, show_id, root, canvas, show_params, track_params, pp_dir, pp_home, pp_profile):

        self.mon = Monitor()
        self.mon.off()

        # instantiate arguments
        self.show_id = show_id
        self.root = root
        self.canvas = canvas
        self.show_params = show_params
        self.track_params = track_params
        self.pp_dir = pp_dir
        self.pp_home = pp_home
        self.pp_profile = pp_profile

        # get duration limit (secs ) from profile
        if self.track_params["duration"] <> "":
            self.duration = int(self.track_params["duration"])
            self.duration_limit = 20 * self.duration
        else:
            self.duration_limit = -1

        # get background image from profile.
        self.background_file = ""
        if self.track_params["background-image"] <> "":
            self.background_file = self.track_params["background-image"]
        else:
            if self.track_params["display-show-background"] == "yes":
                self.background_file = self.show_params["background-image"]

        # get background colour from profile.
        if self.track_params["background-colour"] <> "":
            self.background_colour = self.track_params["background-colour"]
        else:
            self.background_colour = self.show_params["background-colour"]

        # get audio device from profile.
        if self.track_params["mplayer-audio"] <> "":
            self.mplayer_audio = self.track_params["mplayer-audio"]
        else:
            self.mplayer_audio = self.show_params["mplayer-audio"]

        # get audio volume from profile.
        if self.track_params["mplayer-volume"] <> "":
            self.mplayer_volume = self.track_params["mplayer-volume"].strip()
        else:
            self.mplayer_volume = self.show_params["mplayer-volume"].strip()
        self.volume_option = "volume=" + self.mplayer_volume

        # get speaker from profile
        if self.track_params["audio-speaker"] <> "":
            self.audio_speaker = self.track_params["audio-speaker"]
        else:
            self.audio_speaker = self.show_params["audio-speaker"]

        if self.audio_speaker == "left":
            self.speaker_option = AudioPlayer._LEFT
        elif self.audio_speaker == "right":
            self.speaker_option = AudioPlayer._RIGHT
        else:
            self.speaker_option = AudioPlayer._STEREO

        # get animation instructions from profile
        self.animate_begin_text = self.track_params["animate-begin"]
        self.animate_end_text = self.track_params["animate-end"]

        # open the plugin Manager
        self.pim = PluginManager(
            self.show_id,
            self.root,
            self.canvas,
            self.show_params,
            self.track_params,
            self.pp_dir,
            self.pp_home,
            self.pp_profile,
        )

        # create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # could put instance generation in play, not sure which is better.
        self.mplayer = mplayerDriver(self.canvas)
        self.tick_timer = None
        self.init_play_state_machine()

    def play(self, track, showlist, end_callback, ready_callback, enable_menu=False):

        # instantiate arguments
        self.track = track
        self.showlist = showlist
        self.end_callback = end_callback  # callback when finished
        self.ready_callback = ready_callback  # callback when ready to play
        self.enable_menu = enable_menu

        # select the sound device
        if self.mplayer_audio <> "":
            if self.mplayer_audio == "hdmi":
                os.system("amixer -q -c 0 cset numid=3 2")
            else:
                os.system("amixer -q -c 0 cset numid=3 1")

        # callback to the calling object to e.g remove egg timer.
        if self.ready_callback <> None:
            self.ready_callback()

        # create an  instance of showmanager so we can control concurrent shows
        self.show_manager = ShowManager(
            self.show_id,
            self.showlist,
            self.show_params,
            self.root,
            self.canvas,
            self.pp_dir,
            self.pp_profile,
            self.pp_home,
        )

        # Control other shows at beginning
        reason, message = self.show_manager.show_control(self.track_params["show-control-begin"])
        if reason == "error":
            self.mon.err(self, message)
            self.end_callback(reason, message)
            self = None
        else:
            # display image and text
            reason, message = self.display_content()
            if reason == "error":
                self.mon.err(self, message)
                self.end_callback(reason, message)
                self = None
            else:
                # create animation events
                reason, message = self.ppio.animate(self.animate_begin_text, id(self))
                if reason == "error":
                    self.mon.err(self, message)
                    self.end_callback(reason, message)
                    self = None
                else:
                    # start playing the track.
                    if self.duration_limit <> 0:
                        self.start_play_state_machine()
                    else:
                        self.tick_timer = self.canvas.after(10, self.end_zero)

    def end_zero(self):
        self.end("normal", "zero duration")

    def terminate(self, reason):
        """
        terminate the  player in special circumstances
        normal user termination if by key_pressed 'stop'
        reason will be killed or error
        """
        # circumvents state machine to terminate lower level and then itself.
        if self.mplayer <> None:
            self.mon.log(self, "sent terminate to mplayerdriver")
            self.mplayer.terminate(reason)
            self.end("killed", " end without waiting for mplayer to finish")  # end without waiting
        else:
            self.mon.log(self, "terminate, mplayerdriver not running")
            self.end("killed", "terminate, mplayerdriver not running")

    def get_links(self):
        return self.track_params["links"]

    def input_pressed(self, symbol):
        if symbol[0:6] == "mplay-":
            self.control(symbol[6])

        elif symbol == "pause":
            self.pause()

        elif symbol == "stop":
            self.stop()

    # ***************************************
    # INTERNAL FUNCTIONS
    # ***************************************

    # toggle pause
    def pause(self):
        if self.play_state in (AudioPlayer._PLAYING, AudioPlayer._ENDING) and self.track <> "":
            self.mplayer.pause()
            return True
        else:
            self.mon.log(self, "!<pause rejected")
            return False

    # other control when playing, not currently used
    def control(self, char):
        if self.play_state == AudioPlayer._PLAYING and self.track <> "" and char not in ("q"):
            self.mon.log(self, "> send control to mplayer: " + char)
            self.mplayer.control(char)
            return True
        else:
            self.mon.log(self, "!<control rejected")
            return False

    # respond to normal stop
    def stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self, ">stop received")
        self.quit_signal = True

    # ***************************************
    #  sequencing
    # ***************************************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the mplayer process is not running, mplayer process can be initiated
         - _starting - mplayer process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - mplayer is doing its termination, controls cannot be sent
    """

    def init_play_state_machine(self):
        self.quit_signal = False
        self.play_state = AudioPlayer._CLOSED

    def start_play_state_machine(self):
        # initialise all the state machine variables
        self.duration_count = 0
        self.quit_signal = False  # signal that user has pressed stop

        # play the track
        options = (
            self.show_params["mplayer-other-options"] + "-af " + self.speaker_option + "," + self.volume_option + " "
        )
        if self.track <> "":
            self.mplayer.play(self.track, options)
            self.mon.log(self, "Playing track from show Id: " + str(self.show_id))
            self.play_state = AudioPlayer._STARTING
        else:
            self.play_state = AudioPlayer._PLAYING
        # and start polling for state changes and count duration
        self.tick_timer = self.canvas.after(50, self.play_state_machine)

    def play_state_machine(self):
        self.duration_count += 1

        if self.play_state == AudioPlayer._CLOSED:
            self.mon.log(self, "      State machine: " + self.play_state)
            return

        elif self.play_state == AudioPlayer._STARTING:
            self.mon.log(self, "      State machine: " + self.play_state)

            # if mplayer is playing the track change to play state
            if self.mplayer.start_play_signal == True:
                self.mon.log(self, "            <start play signal received from mplayer")
                self.mplayer.start_play_signal = False
                self.play_state = AudioPlayer._PLAYING
                self.mon.log(self, "      State machine: mplayer_playing started")
            self.tick_timer = self.canvas.after(50, self.play_state_machine)

        elif self.play_state == AudioPlayer._PLAYING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # service any queued stop signals
            if self.quit_signal == True or (self.duration_limit > 0 and self.duration_count > self.duration_limit):
                self.mon.log(self, "      Service stop required signal or timeout")
                # self.quit_signal=False
                if self.track <> "":
                    self.stop_mplayer()
                    self.play_state = AudioPlayer._ENDING
                else:
                    self.play_state = AudioPlayer._CLOSED
                    self.end("normal", "stop required signal or timeout")

            # mplayer reports it is terminating so change to ending state
            if self.track <> "" and self.mplayer.end_play_signal:
                self.mon.log(self, "            <end play signal received")
                self.mon.log(self, "            <end detected at: " + str(self.mplayer.audio_position))
                self.play_state = AudioPlayer._ENDING
            self.tick_timer = self.canvas.after(50, self.play_state_machine)

        elif self.play_state == AudioPlayer._ENDING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # if spawned process has closed can change to closed state
            # self.mon.log (self,"      State machine : is mplayer process running? -  "  + str(self.mplayer.is_running()))
            if self.mplayer.is_running() == False:
                self.mon.log(self, "            <mplayer process is dead")
                if self.quit_signal == True:
                    self.quit_signal = False
                    self.play_state = AudioPlayer._CLOSED
                    self.end("normal", "quit required or timeout")
                elif self.duration_limit > 0 and self.duration_count < self.duration_limit:
                    self.play_state = AudioPlayer._WAITING
                    self.tick_timer = self.canvas.after(50, self.play_state_machine)
                else:
                    self.play_state = AudioPlayer._CLOSED
                    self.end("normal", "mplayer dead")
            else:
                self.tick_timer = self.canvas.after(50, self.play_state_machine)

        elif self.play_state == AudioPlayer._WAITING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            if self.quit_signal == True or (self.duration_limit > 0 and self.duration_count > self.duration_limit):
                self.mon.log(self, "      Service stop required signal or timeout from wait")
                self.quit_signal = False
                self.play_state = AudioPlayer._CLOSED
                self.end("normal", "mplayer dead")
            else:
                self.tick_timer = self.canvas.after(50, self.play_state_machine)

    def stop_mplayer(self):
        # send signal to stop the track to the state machine
        self.mon.log(self, "         >stop mplayer received from state machine")
        if self.play_state == AudioPlayer._PLAYING:
            self.mplayer.stop()
            return True
        else:
            self.mon.log(self, "!<stop rejected")
            return False

    # *****************
    # ending the player
    # *****************

    def end(self, reason, message):
        # stop the plugin
        if self.track_params["plugin"] <> "":
            self.pim.stop_plugin()

        # abort the timer
        if self.tick_timer <> None:
            self.canvas.after_cancel(self.tick_timer)
            self.tick_timer = None

        if reason in ("error", "killed"):
            self.end_callback(reason, message)
            self = None

        else:
            # normal end so do show control and animation

            # Control concurrent shows at end
            reason, message = self.show_manager.show_control(self.track_params["show-control-end"])
            if reason == "error":
                self.mon.err(self, message)
                self.end_callback(reason, message)
                self = None
            else:
                # clear events list for this track
                if self.track_params["animate-clear"] == "yes":
                    self.ppio.clear_events_list(id(self))

                # create animation events for ending
                reason, message = self.ppio.animate(self.animate_end_text, id(self))
                if reason == "error":
                    self.mon.err(self, message)
                    self.end_callback(reason, message)
                    self = None
                else:
                    self.end_callback("normal", "track has terminated or quit")
                    self = None

    # *****************
    # displaying things
    # *****************

    def display_content(self):

        # if self.background_file<>'' or self.show_params['show-text']<> '' or #self.track_params['track-text']<> '' or self.enable_menu== True or #self.track_params['clear-screen']=='yes':

        if self.track_params["clear-screen"] == "yes":
            self.canvas.delete("pp-content")
            # self.canvas.update()

        # background colour
        if self.background_colour <> "":
            self.canvas.config(bg=self.background_colour)

        if self.background_file <> "":
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self, "Audio background file not found: " + self.background_img_file)
                self.end("error", "Audio background file not found")
            else:
                pil_background_img = PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(
                    int(self.canvas["width"]) / 2,
                    int(self.canvas["height"]) / 2,
                    image=self.background,
                    anchor=CENTER,
                    tag="pp-content",
                )

        # execute the plugin if required
        if self.track_params["plugin"] <> "":

            reason, message, self.track = self.pim.do_plugin(self.track, self.track_params["plugin"])
            if reason <> "normal":
                return reason, message

        # display hint text if enabled

        if self.enable_menu == True:
            self.canvas.create_text(
                int(self.show_params["hint-x"]),
                int(self.show_params["hint-y"]),
                text=self.show_params["hint-text"],
                fill=self.show_params["hint-colour"],
                font=self.show_params["hint-font"],
                anchor=NW,
                tag="pp-content",
            )

        # display show text if enabled
        if self.show_params["show-text"] <> "" and self.track_params["display-show-text"] == "yes":
            self.canvas.create_text(
                int(self.show_params["show-text-x"]),
                int(self.show_params["show-text-y"]),
                anchor=NW,
                text=self.show_params["show-text"],
                fill=self.show_params["show-text-colour"],
                font=self.show_params["show-text-font"],
                tag="pp-content",
            )

        # display track text if enabled
        if self.track_params["track-text"] <> "":
            self.canvas.create_text(
                int(self.track_params["track-text-x"]),
                int(self.track_params["track-text-y"]),
                anchor=NW,
                text=self.track_params["track-text"],
                fill=self.track_params["track-text-colour"],
                font=self.track_params["track-text-font"],
                tag="pp-content",
            )

        self.mon.log(self, "Displayed background and text ")

        self.canvas.tag_raise("pp-click-area")

        self.canvas.update_idletasks()

        return "normal", ""

    # *****************
    # utilities
    # *****************

    def complete_path(self, track_file):
        #  complete path of the filename of the selected entry
        if track_file[0] == "+":
            track_file = self.pp_home + track_file[1:]
        self.mon.log(self, "Background image is " + track_file)
        return track_file
Ejemplo n.º 19
0
    def __init__(self,
                        show_id,
                        canvas,
                         pp_home,
                        show_params,
                        track_params ):
        """       
            canvas - the canvas onto which the background image is to be drawn
            show_params - configuration of show playing the track
            track_params - config dictionary for this track overrides show_params
        """

        self.mon=Monitor()
        self.mon.on()

        
        #instantiate arguments
        self.show_id=show_id
        self.show_params=show_params       #configuration dictionary for the videoplayer
        self.canvas = canvas  #canvas onto which video should be played but isn't! Use as widget for alarm
        self.pp_home=pp_home
        self.track_params=track_params

        # get duration (secs ) from profile
        self.duration= int(self.track_params['duration'])
        self.duration_limit=20*self.duration


        # get background image from profile.
        self.background_file  = self.track_params['background-image']


        # get audio sink from profile.
        if  self.track_params['mplayer-audio']<>"":
            self.mplayer_audio= self.track_params['mplayer-audio']
        else:
            self.mplayer_audio= self.show_params['mplayer-audio']
            
        # get audio volume from profile.
        if  self.track_params['mplayer-volume']<>"":
            self.mplayer_volume= self.track_params['mplayer-volume'].strip()
        else:
            self.mplayer_volume= self.show_params['mplayer-volume'].strip()
        self.volume_option= 'volume=' + self.mplayer_volume


        #get speaker from profile
        if  self.track_params['audio-speaker']<>"":
            self.audio_speaker= self.track_params['audio-speaker']
        else:
            self.audio_speaker= self.show_params['audio-speaker']

        if self.audio_speaker=='left':
            self.speaker_option=AudioPlayer._LEFT
        elif self.audio_speaker=='right':
            self.speaker_option=AudioPlayer._RIGHT
        else:
            self.speaker_option=AudioPlayer._STEREO

        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']


        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # could put instance generation in play, not sure which is better.
        self.mplayer=mplayerDriver(self.canvas)
        self._tick_timer=None
        self.error=False
        self.terminate_me=False
        self._init_play_state_machine()
Ejemplo n.º 20
0
class Test:
    def __init__(self, track, pp_dir, pp_home, show_params, ct):

        self.track = track
        self.show_params = show_params
        self.pp_home = pp_home
        self.ct = ct
        self.break_from_loop = False

        # create and instance of a Tkinter top level window and refer to it as 'my_window'
        my_window = Tk()
        my_window.title("AudioPlayer Test Harness")

        # change the look of the window
        my_window.configure(background="grey")
        window_width = 1500
        window_height = 900

        canvas_height = window_height
        canvas_width = window_width

        # defne response to main window closing
        my_window.protocol("WM_DELETE_WINDOW", self.terminate)

        my_window.geometry("%dx%d+200+20" % (window_width, window_height))

        # Always use CTRL-Break key to close the program as a get out of jail
        my_window.bind("<Break>", self.e_terminate)

        my_window.bind("s", self.play_event)
        my_window.bind("p", self.pause_event)
        my_window.bind("q", self.stop_event)
        my_window.bind("l", self.loop_event)
        my_window.bind("n", self.next_event)

        # setup a canvas onto which will not be drawn the video!!
        canvas = Canvas(my_window, bg="black")
        canvas.config(height=canvas_height, width=canvas_width)
        canvas.pack()
        # make sure focus is set on canvas.
        canvas.focus_set()
        self.canvas = canvas

        # create an instance of PPIO and start polling
        self.ppio = PPIO()
        self.ppio.init(pp_dir, pp_home, self.canvas, 50, self.callback)
        self.ppio.poll()

        my_window.mainloop()

    def callback(self, index, name, edge):
        print name, edge

    # key presses

    def e_terminate(self, event):
        self.terminate()

    def play_event(self, event):
        self.vp = AudioPlayer(self.canvas, self.pp_home, self.show_params, self.ct)
        self.vp.play(
            self.track, self.on_end, self.do_ready, False, self.do_starting, self.do_playing, self.do_finishing
        )

    # toggles pause
    def pause_event(self, event):
        self.vp.key_pressed("p")

    def stop_event(self, event):
        self.break_from_loop = True
        self.vp.key_pressed("escape")

    def loop_event(self, event):
        # just kick off the first track, callback decides what to do next
        self.break_from_loop = False
        self.vp = AudioPlayer(self.canvas, self.pp_home, self.show_params)
        self.vp.play(
            self.track, self.what_next, self, do_ready, False, self.do_starting, self.do_playing, self.do_finishing
        )

    def next_event(self, event):
        self.break_from_loop = False
        self.vp.key_pressed("down")

    def what_next(self, reason, message):
        self.vp = None
        if reason in ("killed", "error"):
            self.end(reason, message)
        else:
            if self.break_from_loop == True:
                self.break_from_loop = False
                print "test harness: loop interupted"
                return
            else:
                self.vp = AudioPlayer(self.canvas, self.show_params)
                self.vp.play(self.track, self.what_next, self.do_starting, self.do_playing, self.do_finishing)

    def on_end(self, reason, message):
        self.vp = None
        print "Test Class: callback from AudioPlayer says: " + message
        if reason in ("killed", "error"):
            self.end(reason, message)
        else:
            return

    def do_ready(self):
        print "test class message from AudioPlayer: ready to play"
        return

    def do_starting(self):
        print "test class message from AudioPlayer: do starting"
        return

    def do_playing(self):
        # self.display_time.set(self.time_string(self.vp.audio_position))
        # print "test class message from AudioPlayer: do playing"
        return

    def do_finishing(self):
        print "test class message from AudioPlayer: do ending"
        return

    def terminate(self):
        if self.vp == None:
            self.end("killled", "killled")
        else:
            self.vp.terminate("killed")
            return

    def _end(self, reason, message):
        self.ppio.terminate()
        exit()
Ejemplo n.º 21
0
    def __init__(self):

        self.pipresents_issue = "1.2"
        self.pipresents_minorissue = '1.2.3f'
        self.nonfull_window_width = 0.5  # proportion of width
        self.nonfull_window_height = 0.6  # proportion of height
        self.nonfull_window_x = 0  # position of top left corner
        self.nonfull_window_y = 0  # position of top left corner

        StopWatch.global_enable = False

        #****************************************
        # Initialisation
        # ***************************************
        # get command line options
        self.options = command_options()

        # get pi presents code directory
        pp_dir = sys.path[0]
        self.pp_dir = pp_dir

        if not os.path.exists(pp_dir + "/pipresents.py"):
            tkMessageBox.showwarning("Pi Presents",
                                     "Bad Application Directory")
            exit()

        #Initialise logging
        Monitor.log_path = pp_dir
        self.mon = Monitor()
        self.mon.on()
        # 0  - errors only
        # 1  - errors and warnings
        # 2  - everything
        if self.options['debug'] == True:
            Monitor.global_enable = 2
        else:
            Monitor.global_enable = 0

        # UNCOMMENT THIS TO LOG WARNINGS AND ERRORS ONLY
        # Monitor.global_enable=1

        self.mon.log(
            self,
            "\n\n\n\n\n*****************\nPi Presents is starting, Version:" +
            self.pipresents_minorissue)
        self.mon.log(self, "Version: " + self.pipresents_minorissue)
        self.mon.log(self, " OS and separator:" + os.name + '  ' + os.sep)
        self.mon.log(self, "sys.path[0] -  location of code: " + sys.path[0])
        # self.mon.log(self,"os.getenv('HOME') -  user home directory (not used): " + os.getenv('HOME'))
        # self.mon.log(self,"os.path.expanduser('~') -  user home directory: " + os.path.expanduser('~'))

        # optional other classes used
        self.ppio = None
        self.tod = None

        #get profile path from -p option
        if self.options['profile'] <> "":
            self.pp_profile_path = "/pp_profiles/" + self.options['profile']
        else:
            self.pp_profile_path = "/pp_profiles/pp_profile"

    #get directory containing pp_home from the command,
        if self.options['home'] == "":
            home = os.path.expanduser('~') + os.sep + "pp_home"
        else:
            home = self.options['home'] + os.sep + "pp_home"
        self.mon.log(self, "pp_home directory is: " + home)

        #check if pp_home exists.
        # try for 10 seconds to allow usb stick to automount
        # fall back to pipresents/pp_home
        self.pp_home = pp_dir + "/pp_home"
        found = False
        for i in range(1, 10):
            self.mon.log(self,
                         "Trying pp_home at: " + home + " (" + str(i) + ')')
            if os.path.exists(home):
                found = True
                self.pp_home = home
                break
            time.sleep(1)
        if found == True:
            self.mon.log(
                self,
                "Found Requested Home Directory, using pp_home at: " + home)
        else:
            self.mon.log(
                self,
                "FAILED to find requested home directory, using default to display error message: "
                + self.pp_home)

        #check profile exists, if not default to error profile inside pipresents
        self.pp_profile = self.pp_home + self.pp_profile_path
        if os.path.exists(self.pp_profile):
            self.mon.log(
                self, "Found Requested profile - pp_profile directory is: " +
                self.pp_profile)
        else:
            self.pp_profile = pp_dir + "/pp_home/pp_profiles/pp_profile"
            self.mon.log(
                self,
                "FAILED to find requested profile, using default to display error message: pp_profile"
            )

        if self.options['verify'] == True:
            val = Validator()
            if val.validate_profile(None, pp_dir, self.pp_home,
                                    self.pp_profile, self.pipresents_issue,
                                    False) == False:
                tkMessageBox.showwarning("Pi Presents", "Validation Failed")
                exit()

        # open the resources
        self.rr = ResourceReader()
        # read the file, done once for all the other classes to use.
        if self.rr.read(pp_dir, self.pp_home, self.pp_profile) == False:
            self.end('error', 'cannot find resources.cfg')

        #initialise and read the showlist in the profile
        self.showlist = ShowList()
        self.showlist_file = self.pp_profile + "/pp_showlist.json"
        if os.path.exists(self.showlist_file):
            self.showlist.open_json(self.showlist_file)
        else:
            self.mon.err(self, "showlist not found at " + self.showlist_file)
            self.end('error', 'showlist not found')

        # check profile and Pi Presents issues are compatible
        if float(self.showlist.sissue()) <> float(self.pipresents_issue):
            self.mon.err(
                self, "Version of profile " + self.showlist.sissue() +
                " is not  same as Pi Presents, must exit")
            self.end('error', 'wrong version of profile')

        # get the 'start' show from the showlist
        index = self.showlist.index_of_show('start')
        if index >= 0:
            self.showlist.select(index)
            self.starter_show = self.showlist.selected_show()
        else:
            self.mon.err(self, "Show [start] not found in showlist")
            self.end('error', 'start show not found')

# ********************
# SET UP THE GUI
# ********************
#turn off the screenblanking and saver
        if self.options['noblank'] == True:
            call(["xset", "s", "off"])
            call(["xset", "s", "-dpms"])

        self.root = Tk()

        self.title = 'Pi Presents - ' + self.pp_profile
        self.icon_text = 'Pi Presents'
        self.root.title(self.title)
        self.root.iconname(self.icon_text)
        self.root.config(bg='black')

        # get size of the screen
        self.screen_width = self.root.winfo_screenwidth()
        self.screen_height = self.root.winfo_screenheight()

        # set window dimensions and decorations
        if self.options['fullscreen'] == True:

            self.root.attributes('-fullscreen', True)
            os.system('unclutter &')
            self.window_width = self.screen_width
            self.window_height = self.screen_height
            self.window_x = 0
            self.window_y = 0
            self.root.geometry("%dx%d%+d%+d" %
                               (self.window_width, self.window_height,
                                self.window_x, self.window_y))
            self.root.attributes('-zoomed', '1')
        else:
            self.window_width = int(self.screen_width *
                                    self.nonfull_window_width)
            self.window_height = int(self.screen_height *
                                     self.nonfull_window_height)
            self.window_x = self.nonfull_window_x
            self.window_y = self.nonfull_window_y
            self.root.geometry("%dx%d%+d%+d" %
                               (self.window_width, self.window_height,
                                self.window_x, self.window_y))

        #canvas covers the whole window
        self.canvas_height = self.screen_height
        self.canvas_width = self.screen_width

        # make sure focus is set.
        self.root.focus_set()

        #define response to main window closing.
        self.root.protocol("WM_DELETE_WINDOW", self.exit_pressed)

        #setup a canvas onto which will be drawn the images or text
        self.canvas = Canvas(self.root, bg='black')

        self.canvas.config(height=self.canvas_height,
                           width=self.canvas_width,
                           highlightthickness=0)
        # self.canvas.pack()
        self.canvas.place(x=0, y=0)

        self.canvas.focus_set()

        # ****************************************
        # INITIALISE THE INPUT DRIVERS
        # ****************************************

        # looks after bindings between symbolic names and internal operations
        controlsmanager = ControlsManager()
        if controlsmanager.read(pp_dir, self.pp_home,
                                self.pp_profile) == False:
            self.end('error', 'cannot find or error in controls.cfg.cfg')
        else:
            controlsmanager.parse_defaults()

        # each driver takes a set of inputs, binds them to symboic names
        # and sets up a callback which returns the symbolic name when an input event occurs/

        # use keyboard driver to bind keys to symbolic names and to set up callback
        kbd = KbdDriver()
        if kbd.read(pp_dir, self.pp_home, self.pp_profile) == False:
            self.end('error', 'cannot find or error in keys.cfg')
        kbd.bind_keys(self.root, self.input_pressed)

        self.sr = ScreenDriver()
        # read the screen click area config file
        if self.sr.read(pp_dir, self.pp_home, self.pp_profile) == False:
            self.end('error', 'cannot find screen.cfg')

        # create click areas on the canvas, must be polygon as outline rectangles are not filled as far as find_closest goes
        reason, message = self.sr.make_click_areas(self.canvas,
                                                   self.input_pressed)
        if reason == 'error':
            self.mon.err(self, message)
            self.end('error', message)

# ****************************************
# INITIALISE THE APPLICATION AND START
# ****************************************
        self.shutdown_required = False

        #kick off GPIO if enabled by command line option
        if self.options['gpio'] == True:
            from pp_gpio import PPIO
            # initialise the GPIO
            self.ppio = PPIO()
            # PPIO.gpio_enabled=False
            if self.ppio.init(pp_dir, self.pp_home, self.pp_profile,
                              self.canvas, 50, self.gpio_pressed) == False:
                self.end('error', 'gpio error')

            # and start polling gpio
            self.ppio.poll()

        #kick off the time of day scheduler
        self.tod = TimeOfDay()
        self.tod.init(pp_dir, self.pp_home, self.canvas, 500)
        self.tod.poll()

        # Create list of start shows initialise them and then run them
        self.run_start_shows()

        #start tkinter
        self.root.mainloop()
Ejemplo n.º 22
0
    def __init__(self,
                         show_id,
                         root,
                        canvas,
                        show_params,
                        track_params ,
                         pp_dir,
                        pp_home,
                        pp_profile):

        self.mon=Monitor()
        self.mon.on()
        
        #instantiate arguments
        self.show_id=show_id
        self.root=root
        self.canvas = canvas
        self.show_params=show_params
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile


        # get config from medialist if there.
        if self.track_params['omx-audio']<>"":
            self.omx_audio= self.track_params['omx-audio']
        else:
            self.omx_audio= self.show_params['omx-audio']
        if self.omx_audio<>"": self.omx_audio= "-o "+ self.omx_audio
        
        if self.track_params['omx-volume']<>"":
            self.omx_volume= self.track_params['omx-volume']
        else:
            self.omx_volume= self.show_params['omx-volume']
        if self.omx_volume<>"":
            self.omx_volume= "--vol "+ str(int(self.omx_volume)*100) + ' '

        if self.track_params['omx-window']<>'':
            self.omx_window= self.track_params['omx-window']
        else:
            self.omx_window= self.show_params['omx-window']


        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>"":
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']
        
        self.centre_x = int(self.canvas['width'])/2
        self.centre_y = int(self.canvas['height'])/2
        
        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()        
        
        # could put instance generation in play, not sure which is better.
        self.omx=OMXDriver(self.canvas)
        self.tick_timer=None
        self.init_play_state_machine()
Ejemplo n.º 23
0
class PiPresents:

    # Constants for list of start shows
    SHOW_TEMPLATE=['',None,-1]
    NAME = 0   # text name of the show
    SHOW = 1   # the show object
    ID = 2    # Numeic identity of the show object, sent to the instance and returned in callbacks
    
    def __init__(self):
        
        self.pipresents_issue="1.2"
        
        StopWatch.global_enable=False

#****************************************
# INTERPRET COMMAND LINE
# ***************************************

        self.options=command_options()
        

        pp_dir=sys.path[0]
        
        if not os.path.exists(pp_dir+"/pipresents.py"):
            tkMessageBox.showwarning("Pi Presents","Bad Application Directory")
            exit()

        
        #Initialise logging
        Monitor.log_path=pp_dir
        self.mon=Monitor()
        self.mon.on()
        if self.options['debug']==True:
            Monitor.global_enable=True
        else:
            Monitor.global_enable=False
 
        self.mon.log (self, "Pi Presents is starting")
        self.mon.log (self," OS and separator:" + os.name +'  ' + os.sep)
        self.mon.log(self,"sys.path[0] -  location of code: "+sys.path[0])
        # self.mon.log(self,"os.getenv('HOME') -  user home directory (not used): " + os.getenv('HOME'))
        # self.mon.log(self,"os.path.expanduser('~') -  user home directory: " + os.path.expanduser('~'))
        
        self.ppio=None
        self.tod=None
 
        # create  profile  for pp_editor test files if already not there.
        if not os.path.exists(pp_dir+"/pp_home/pp_profiles/pp_editor"):
            self.mon.log(self,"Making pp_editor directory") 
            os.makedirs(pp_dir+"/pp_home/pp_profiles/pp_editor")
            
            
        #profile path from -p option
        if self.options['profile']<>"":
            self.pp_profile_path="/pp_profiles/"+self.options['profile']
        else:
            self.pp_profile_path = "/pp_profiles/pp_profile"
        
       #get directory containing pp_home from the command,
        if self.options['home'] =="":
            home = os.path.expanduser('~')+ os.sep+"pp_home"
        else:
            home = self.options['home'] + os.sep+ "pp_home"
            
        self.mon.log(self,"pp_home directory is: " + home)          
        #check if pp_home exists.
        # try for 10 seconds to allow usb stick to automount
        # fall back to pipresents/pp_home
        self.pp_home=pp_dir+"/pp_home"
        for i in range (1, 10):
            self.mon.log(self,"Trying pp_home at: " + home +  " (" + str(i)+')')
            if os.path.exists(home):
                self.mon.log(self,"Using pp_home at: " + home)
                self.pp_home=home
                break
            time.sleep (1)

        #check profile exists, if not default to error profile inside pipresents
        self.pp_profile=self.pp_home+self.pp_profile_path
        if not os.path.exists(self.pp_profile):
            self.pp_profile=pp_dir+"/pp_home/pp_profiles/pp_profile"

        if self.options['verify']==True:
            val =Validator()
            if  val.validate_profile(None,pp_dir,self.pp_home,self.pp_profile,self.pipresents_issue,False) == False:
                tkMessageBox.showwarning("Pi Presents","Validation Failed")
                exit()
                
        # open the resources
        self.rr=ResourceReader()
        # read the file, done once for all the other classes to use.
        if self.rr.read(pp_dir,self.pp_home)==False:
            #self.mon.err(self,"Version of profile " + self.showlist.sissue() + " is not  same as Pi Presents, must exit")
            self._end('error','cannot find resources.cfg')            

        
        #initialise the showlists and read the showlists
        self.showlist=ShowList()
        self.showlist_file= self.pp_profile+ "/pp_showlist.json"
        if os.path.exists(self.showlist_file):
            self.showlist.open_json(self.showlist_file)
        else:
            self.mon.err(self,"showlist not found at "+self.showlist_file)
            self._end('error','showlist not found')

        if float(self.showlist.sissue())<>float(self.pipresents_issue):
            self.mon.err(self,"Version of profile " + self.showlist.sissue() + " is not  same as Pi Presents, must exit")
            self._end('error','wrong version of profile')
 
        # get the 'start' show from the showlist
        index = self.showlist.index_of_show('start')
        if index >=0:
            self.showlist.select(index)
            self.starter_show=self.showlist.selected_show()
        else:
            self.mon.err(self,"Show [start] not found in showlist")
            self._end('error','start show not found')

        
# ********************
# SET UP THE GUI
# ********************
        #turn off the screenblanking and saver
        if self.options['noblank']==True:
            call(["xset","s", "off"])
            call(["xset","s", "-dpms"])

        self.root=Tk()
        # control display of window decorations
        if self.options['fullscreen']==True:
            self.root.attributes('-fullscreen', True)
            #self.root = Tk(className="fspipresents")
            os.system('unclutter &')
        else:
            #self.root = Tk(className="pipresents")
            pass


        self.title='Pi Presents - '+ self.pp_profile
        self.icon_text= 'Pi Presents'
        
        self.root.title(self.title)
        self.root.iconname(self.icon_text)
        self.root.config(bg='black')
        
        # get size of the screen
        self.screen_width = self.root.winfo_screenwidth()
        self.screen_height = self.root.winfo_screenheight()

        # set window dimensions
        self.window_height=self.screen_height
        self.window_width=self.screen_width
        self.window_x=0
        self.window_y=0
        if self.options['fullscreen']==True:
            bar=self.options['fullscreen']
            # allow just 2 pixels for the hidden taskbar - not any more
            if bar in ('left','right'):
                self.window_width=self.screen_width
            else:
                self.window_height=self.screen_height
            if bar =="left":
                self.window_x=0
            if bar =="top":
                self.window_y=0  
            self.root.geometry("%dx%d%+d%+d"  % (self.window_width,self.window_height,self.window_x,self.window_y))
            self.root.attributes('-zoomed','1')
        else:
            self.window_width=self.screen_width-600
            self.window_height=self.screen_height-200
            self.window_x=50
            self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y))
            

        #canvas covers the whole window
        self.canvas_height=self.window_height
        self.canvas_width=self.window_width
        
        # make sure focus is set.
        self.root.focus_set()

        #define response to main window closing.
        self.root.protocol ("WM_DELETE_WINDOW", self.on_break_key)

        # Always use CTRL-Break key to close the program as a get out of jail
        self.root.bind("<Break>",self.e_on_break_key)
        
        #pass all other keys along to start shows and hence to 'players'
        self.root.bind("<Escape>", self._escape_pressed)
        self.root.bind("<Up>", self._up_pressed)
        self.root.bind("<Down>", self._down_pressed)
        self.root.bind("<Return>", self._return_pressed)
        self.root.bind("<space>", self._pause_pressed)
        self.root.bind("p", self._pause_pressed)

        #setup a canvas onto which will be drawn the images or text
        self.canvas = Canvas(self.root, bg='black')

        self.canvas.config(height=self.canvas_height, width=self.canvas_width)
        self.canvas.pack()
        # make sure focus is set on canvas.
        self.canvas.focus_set()


# ****************************************
# INITIALISE THE APPLICATION AND START
# ****************************************
        self.shutdown_required=False
        
        #kick off GPIO if enabled by command line option
        if self.options['gpio']==True:
            from pp_gpio import PPIO
            # initialise the GPIO
            self.ppio=PPIO()
            # PPIO.gpio_enabled=False
            if self.ppio.init(pp_dir,self.pp_profile,self.canvas,50,self.button_pressed)==False:
                self._end('error','gpio error')
                
            # and start polling gpio
            self.ppio.poll()

        #kick off the time of day scheduler
        self.tod=TimeOfDay()
        self.tod.init(pp_dir,self.pp_home,self.canvas,500)
        self.tod.poll()

        # Create list of start shows initialise them and then run them
        self.start_shows = self.create_start_show_list()
        self.init_shows()
        self.run_shows()
        self.root.mainloop( )




# *********************
# EXIT APP
# *********************

    # kill or error
    def terminate(self,reason):
        needs_termination=False
        for start_show in self.start_shows:
            if start_show[PiPresents.SHOW]<>None:
                needs_termination=True
                self.mon.log(self,"Sent terminate to show "+ start_show[PiPresents.NAME])
                start_show[PiPresents.SHOW].terminate(reason)
        if needs_termination==False:
            self._end(reason)



    def tidy_up(self):
        #turn screen blanking back on
        if self.options['noblank']==True:
            call(["xset","s", "on"])
            call(["xset","s", "+dpms"])
        # tidy up gpio
        if self.options['gpio']==True and self.ppio<>None:
            self.ppio.terminate()
        #tidy up time of day scheduler
        if self.tod<>None:
            self.tod.terminate()
        #close logging files 
        self.mon.finish()

        
    def on_kill_callback(self):
        self.tidy_up()
        if self.shutdown_required==True:
            call(['sudo', 'shutdown', '-h', '-t 5','now'])
        else:
            exit()

    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

# *********************
# Key and button presses
# ********************

    def shutdown_pressed(self):
        self.root.after(5000,self.on_shutdown_delay)

    def on_shutdown_delay(self):
        if self.ppio.is_pressed('shutdown'):
            self.shutdown_required=True
            self.on_break_key()

    def button_pressed(self,index,button,edge):
        self.mon.log(self, "Button Pressed: "+button)
        if button=="shutdown":
            self.shutdown_pressed()
        else:
            for start_show in self.start_shows:
                print "sending to show" , start_show[PiPresents.NAME]
                start_show[PiPresents.SHOW].button_pressed(button,edge)                 

  
    # key presses - convert from events to call to _key_pressed
    def _escape_pressed(self,event): self._key_pressed("escape")              
    def _up_pressed(self,event): self._key_pressed("up")  
    def _down_pressed(self,event): self._key_pressed("down")  
    def _return_pressed(self,event): self._key_pressed("return")
    def _pause_pressed(self,event): self._key_pressed("p")
        

    def _key_pressed(self,key_name):
        # key pressses are sent only to the controlled show.
        self.mon.log(self, "Key Pressed: "+ key_name)
        for start_show in self.start_shows:
                start_show[PiPresents.SHOW].key_pressed(key_name)          


         
    def on_break_key(self):
        self.mon.log(self, "kill received from user")
        #terminate any running shows and players     
        self.mon.log(self,"kill sent to shows")   
        self.terminate('killed')
 
 
    def e_on_break_key(self,event):
        self.on_break_key()

# *********************
# Start show creation and running and return from
# ********************   
        
# Extract shows from start show
    def create_start_show_list(self):
        start_shows_text=self.starter_show['start-show']
        shows=[]
        index=0
        fields= start_shows_text.split()
        for field in fields:
            show = PiPresents.SHOW_TEMPLATE        
            show[PiPresents.NAME]=field
            show[PiPresents.ID]=index 
            shows.append(copy.deepcopy(show))
            index+=1          
        return shows

    def init_shows(self):
        # build  list of shows to run by instantiating their classes.
        for start_show in self.start_shows:
            index = self.showlist.index_of_show(start_show[PiPresents.NAME])
            if index >=0:
                self.showlist.select(index)
                show=self.showlist.selected_show()
            else:
                self.mon.err(self,"Show not found in showlist: "+ start_show[PiPresents.NAME])
                self._end('error','show not found in showlist')
                
            if show['type']=="mediashow":
                show_obj = MediaShow(show,
                                                                self.canvas,
                                                                self.showlist,
                                                                self.pp_home,
                                                                self.pp_profile)
                start_show[PiPresents.SHOW]=show_obj


                
            elif show['type']=="menu":
                show_obj = MenuShow(show,
                                                        self.canvas,
                                                        self.showlist,
                                                        self.pp_home,
                                                        self.pp_profile)
                start_show[PiPresents.SHOW]=show_obj


            elif show['type']=="liveshow":
                show_obj= LiveShow(show,
                                                        self.canvas,
                                                        self.showlist,
                                                        self.pp_home,
                                                        self.pp_profile)
                start_show[PiPresents.SHOW]=show_obj                   
            else:
                self.mon.err(self,"unknown mediashow type in start show - "+ show['type'])
                self._end('error','unknown mediashow type')


    # run each of the shows in the list
    def run_shows(self):
        for start_show in self.start_shows:
                show_obj = start_show[PiPresents.SHOW]
                show_obj.play(start_show[PiPresents.ID],self._end_play_show,top=True,command='nil')


    def _end_play_show(self,show_id,reason,message):     
        self.mon.log(self,"Show " + str(show_id) + " returned to Pipresents with reason: " + reason )
        self.start_shows[show_id][PiPresents.SHOW]=None
        # if all the shows have ended then end Pi Presents
        all_terminated=True
        for start_show in self.start_shows:
            if start_show[PiPresents.SHOW]<>None:
                all_terminated=False
        if all_terminated==True:
            self._end(reason,message)
     
    def _end(self,reason,message):
        self.mon.log(self,"Pi Presents ending with message: " + message)
        if reason=='error':
            self.mon.log(self, "exiting because of error")
            self.tidy_up()
            exit()            
        if reason=='killed':
            self.mon.log(self,"kill received - exiting")
            self.on_kill_callback()
        else:
            # should never be here or fatal error
            self.mon.log(self, "exiting because invalid end reasosn")
            self.tidy_up()
            exit()
Ejemplo n.º 24
0
    def __init__(self):
        
        self.pipresents_issue="1.2"
        self.nonfull_window_width = 0.6 # proportion of width
        self.nonfull_window_height= 0.6 # proportion of height
        self.nonfull_window_x = 0 # position of top left corner
        self.nonfull_window_y=0   # position of top left corner
        
        StopWatch.global_enable=False

#****************************************
# Initialisation
# ***************************************
        # get command line options
        self.options=command_options()

        # get pi presents code directory
        pp_dir=sys.path[0]
        self.pp_dir=pp_dir
        
        if not os.path.exists(pp_dir+"/pipresents.py"):
            tkMessageBox.showwarning("Pi Presents","Bad Application Directory")
            exit()

        
        #Initialise logging
        Monitor.log_path=pp_dir
        self.mon=Monitor()
        self.mon.on()
        if self.options['debug']==True:
            Monitor.global_enable=True
        else:
            Monitor.global_enable=False
 
        self.mon.log (self, "Pi Presents is starting")
        self.mon.log (self," OS and separator:" + os.name +'  ' + os.sep)
        self.mon.log(self,"sys.path[0] -  location of code: "+sys.path[0])
        # self.mon.log(self,"os.getenv('HOME') -  user home directory (not used): " + os.getenv('HOME'))
        # self.mon.log(self,"os.path.expanduser('~') -  user home directory: " + os.path.expanduser('~'))

        # optional other classes used
        self.ppio=None
        self.tod=None
         
        #get profile path from -p option
        if self.options['profile']<>"":
            self.pp_profile_path="/pp_profiles/"+self.options['profile']
        else:
            self.pp_profile_path = "/pp_profiles/pp_profile"
        
       #get directory containing pp_home from the command,
        if self.options['home'] =="":
            home = os.path.expanduser('~')+ os.sep+"pp_home"
        else:
            home = self.options['home'] + os.sep+ "pp_home"         
        self.mon.log(self,"pp_home directory is: " + home)
        
        #check if pp_home exists.
        # try for 10 seconds to allow usb stick to automount
        # fall back to pipresents/pp_home
        self.pp_home=pp_dir+"/pp_home"
        found=False
        for i in range (1, 10):
            self.mon.log(self,"Trying pp_home at: " + home +  " (" + str(i)+')')
            if os.path.exists(home):
                found=True
                self.pp_home=home
                break
            time.sleep (1)
        if found==True:
            self.mon.log(self,"Found Requested Home Directory, using pp_home at: " + home)
        else:    
            self.mon.log(self,"FAILED to find requested home directory, using default to display error message: " + self.pp_home)


        #check profile exists, if not default to error profile inside pipresents
        self.pp_profile=self.pp_home+self.pp_profile_path
        if os.path.exists(self.pp_profile):
            self.mon.log(self,"Found Requested profile - pp_profile directory is: " + self.pp_profile)
        else:
            self.pp_profile=pp_dir+"/pp_home/pp_profiles/pp_profile"   
            self.mon.log(self,"FAILED to find requested profile, using default to display error message: pp_profile")
        
        if self.options['verify']==True:
            val =Validator()
            if  val.validate_profile(None,pp_dir,self.pp_home,self.pp_profile,self.pipresents_issue,False) == False:
                tkMessageBox.showwarning("Pi Presents","Validation Failed")
                exit()
                
        # open the resources
        self.rr=ResourceReader()
        # read the file, done once for all the other classes to use.
        if self.rr.read(pp_dir,self.pp_home,self.pp_profile)==False:
            self.end('error','cannot find resources.cfg')            

        #initialise and read the showlist in the profile
        self.showlist=ShowList()
        self.showlist_file= self.pp_profile+ "/pp_showlist.json"
        if os.path.exists(self.showlist_file):
            self.showlist.open_json(self.showlist_file)
        else:
            self.mon.err(self,"showlist not found at "+self.showlist_file)
            self.end('error','showlist not found')

        # check profile and Pi Presents issues are compatible
        if float(self.showlist.sissue())<>float(self.pipresents_issue):
            self.mon.err(self,"Version of profile " + self.showlist.sissue() + " is not  same as Pi Presents, must exit")
            self.end('error','wrong version of profile')
 
        # get the 'start' show from the showlist
        index = self.showlist.index_of_show('start')
        if index >=0:
            self.showlist.select(index)
            self.starter_show=self.showlist.selected_show()
        else:
            self.mon.err(self,"Show [start] not found in showlist")
            self.end('error','start show not found')

        
# ********************
# SET UP THE GUI
# ********************
        #turn off the screenblanking and saver
        if self.options['noblank']==True:
            call(["xset","s", "off"])
            call(["xset","s", "-dpms"])

        self.root=Tk()   
       
        self.title='Pi Presents - '+ self.pp_profile
        self.icon_text= 'Pi Presents'
        self.root.title(self.title)
        self.root.iconname(self.icon_text)
        self.root.config(bg='black')
        
        # get size of the screen
        self.screen_width = self.root.winfo_screenwidth()
        self.screen_height = self.root.winfo_screenheight()

        # set window dimensions and decorations
        if self.options['fullscreen']==True:

            self.root.attributes('-fullscreen', True)
            os.system('unclutter &')
            self.window_width=self.screen_width
            self.window_height=self.screen_height
            self.window_x=0
            self.window_y=0  
            self.root.geometry("%dx%d%+d%+d"  % (self.window_width,self.window_height,self.window_x,self.window_y))
            self.root.attributes('-zoomed','1')
        else:
            self.window_width=int(self.screen_width*self.nonfull_window_width)
            self.window_height=int(self.screen_height*self.nonfull_window_height)
            self.window_x=self.nonfull_window_x
            self.window_y=self.nonfull_window_y
            self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y))

            
        #canvas covers the whole window
        self.canvas_height=self.screen_height
        self.canvas_width=self.screen_width
        
        # make sure focus is set.
        self.root.focus_set()

        #define response to main window closing.
        self.root.protocol ("WM_DELETE_WINDOW", self.exit_pressed)

        #setup a canvas onto which will be drawn the images or text
        self.canvas = Canvas(self.root, bg='black')

        self.canvas.config(height=self.canvas_height,
                                       width=self.canvas_width,
                                       highlightthickness=0)
        # self.canvas.pack()
        self.canvas.place(x=0,y=0)

        self.canvas.focus_set()

                
# ****************************************
# INITIALISE THE INPUT DRIVERS
# ****************************************

        # looks after bindings between symbolic names and internal operations
        controlsmanager=ControlsManager()
        if controlsmanager.read(pp_dir,self.pp_home,self.pp_profile)==False:
                self.end('error','cannot find or error in controls.cfg.cfg')
        else:
            controlsmanager.parse_defaults()

        # each driver takes a set of inputs, binds them to symboic names
        # and sets up a callback which returns the symbolic name when an input event occurs/

        # use keyboard driver to bind keys to symbolic names and to set up callback
        kbd=KbdDriver()
        if kbd.read(pp_dir,self.pp_home,self.pp_profile)==False:
                self.end('error','cannot find or error in keys.cfg')
        kbd.bind_keys(self.root,self.input_pressed)

        self.sr=ScreenDriver()
        # read the screen click area config file
        if self.sr.read(pp_dir,self.pp_home,self.pp_profile)==False:
            self.end('error','cannot find screen.cfg')

        # create click areas on the canvas, must be polygon as outline rectangles are not filled as far as find_closest goes
        reason,message = self.sr.make_click_areas(self.canvas,self.input_pressed)
        if reason=='error':
            self.mon.err(self,message)
            self.end('error',message)


# ****************************************
# INITIALISE THE APPLICATION AND START
# ****************************************
        self.shutdown_required=False
        
        #kick off GPIO if enabled by command line option
        if self.options['gpio']==True:
            from pp_gpio import PPIO
            # initialise the GPIO
            self.ppio=PPIO()
            # PPIO.gpio_enabled=False
            if self.ppio.init(pp_dir,self.pp_home,self.pp_profile,self.canvas,50,self.gpio_pressed)==False:
                self.end('error','gpio error')
                
            # and start polling gpio
            self.ppio.poll()

        #kick off the time of day scheduler
        self.tod=TimeOfDay()
        self.tod.init(pp_dir,self.pp_home,self.canvas,500)
        self.tod.poll()


        # Create list of start shows initialise them and then run them
        self.run_start_shows()

        #start tkinter
        self.root.mainloop( )
Ejemplo n.º 25
0
    def __init__(self):
        
        self.pipresents_issue="1.2"
        
        StopWatch.global_enable=False

#****************************************
# INTERPRET COMMAND LINE
# ***************************************

        self.options=command_options()
        

        pp_dir=sys.path[0]
        
        if not os.path.exists(pp_dir+"/pipresents.py"):
            tkMessageBox.showwarning("Pi Presents","Bad Application Directory")
            exit()

        
        #Initialise logging
        Monitor.log_path=pp_dir
        self.mon=Monitor()
        self.mon.on()
        if self.options['debug']==True:
            Monitor.global_enable=True
        else:
            Monitor.global_enable=False
 
        self.mon.log (self, "Pi Presents is starting")
        self.mon.log (self," OS and separator:" + os.name +'  ' + os.sep)
        self.mon.log(self,"sys.path[0] -  location of code: "+sys.path[0])
        # self.mon.log(self,"os.getenv('HOME') -  user home directory (not used): " + os.getenv('HOME'))
        # self.mon.log(self,"os.path.expanduser('~') -  user home directory: " + os.path.expanduser('~'))
        
        self.ppio=None
        self.tod=None
 
        # create  profile  for pp_editor test files if already not there.
        if not os.path.exists(pp_dir+"/pp_home/pp_profiles/pp_editor"):
            self.mon.log(self,"Making pp_editor directory") 
            os.makedirs(pp_dir+"/pp_home/pp_profiles/pp_editor")
            
            
        #profile path from -p option
        if self.options['profile']<>"":
            self.pp_profile_path="/pp_profiles/"+self.options['profile']
        else:
            self.pp_profile_path = "/pp_profiles/pp_profile"
        
       #get directory containing pp_home from the command,
        if self.options['home'] =="":
            home = os.path.expanduser('~')+ os.sep+"pp_home"
        else:
            home = self.options['home'] + os.sep+ "pp_home"
            
        self.mon.log(self,"pp_home directory is: " + home)          
        #check if pp_home exists.
        # try for 10 seconds to allow usb stick to automount
        # fall back to pipresents/pp_home
        self.pp_home=pp_dir+"/pp_home"
        for i in range (1, 10):
            self.mon.log(self,"Trying pp_home at: " + home +  " (" + str(i)+')')
            if os.path.exists(home):
                self.mon.log(self,"Using pp_home at: " + home)
                self.pp_home=home
                break
            time.sleep (1)

        #check profile exists, if not default to error profile inside pipresents
        self.pp_profile=self.pp_home+self.pp_profile_path
        if not os.path.exists(self.pp_profile):
            self.pp_profile=pp_dir+"/pp_home/pp_profiles/pp_profile"

        if self.options['verify']==True:
            val =Validator()
            if  val.validate_profile(None,pp_dir,self.pp_home,self.pp_profile,self.pipresents_issue,False) == False:
                tkMessageBox.showwarning("Pi Presents","Validation Failed")
                exit()
                
        # open the resources
        self.rr=ResourceReader()
        # read the file, done once for all the other classes to use.
        if self.rr.read(pp_dir,self.pp_home)==False:
            #self.mon.err(self,"Version of profile " + self.showlist.sissue() + " is not  same as Pi Presents, must exit")
            self._end('error','cannot find resources.cfg')            

        
        #initialise the showlists and read the showlists
        self.showlist=ShowList()
        self.showlist_file= self.pp_profile+ "/pp_showlist.json"
        if os.path.exists(self.showlist_file):
            self.showlist.open_json(self.showlist_file)
        else:
            self.mon.err(self,"showlist not found at "+self.showlist_file)
            self._end('error','showlist not found')

        if float(self.showlist.sissue())<>float(self.pipresents_issue):
            self.mon.err(self,"Version of profile " + self.showlist.sissue() + " is not  same as Pi Presents, must exit")
            self._end('error','wrong version of profile')
 
        # get the 'start' show from the showlist
        index = self.showlist.index_of_show('start')
        if index >=0:
            self.showlist.select(index)
            self.starter_show=self.showlist.selected_show()
        else:
            self.mon.err(self,"Show [start] not found in showlist")
            self._end('error','start show not found')

        
# ********************
# SET UP THE GUI
# ********************
        #turn off the screenblanking and saver
        if self.options['noblank']==True:
            call(["xset","s", "off"])
            call(["xset","s", "-dpms"])

        self.root=Tk()
        # control display of window decorations
        if self.options['fullscreen']==True:
            self.root.attributes('-fullscreen', True)
            #self.root = Tk(className="fspipresents")
            os.system('unclutter &')
        else:
            #self.root = Tk(className="pipresents")
            pass


        self.title='Pi Presents - '+ self.pp_profile
        self.icon_text= 'Pi Presents'
        
        self.root.title(self.title)
        self.root.iconname(self.icon_text)
        self.root.config(bg='black')
        
        # get size of the screen
        self.screen_width = self.root.winfo_screenwidth()
        self.screen_height = self.root.winfo_screenheight()

        # set window dimensions
        self.window_height=self.screen_height
        self.window_width=self.screen_width
        self.window_x=0
        self.window_y=0
        if self.options['fullscreen']==True:
            bar=self.options['fullscreen']
            # allow just 2 pixels for the hidden taskbar - not any more
            if bar in ('left','right'):
                self.window_width=self.screen_width
            else:
                self.window_height=self.screen_height
            if bar =="left":
                self.window_x=0
            if bar =="top":
                self.window_y=0  
            self.root.geometry("%dx%d%+d%+d"  % (self.window_width,self.window_height,self.window_x,self.window_y))
            self.root.attributes('-zoomed','1')
        else:
            self.window_width=self.screen_width-600
            self.window_height=self.screen_height-200
            self.window_x=50
            self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y))
            

        #canvas covers the whole window
        self.canvas_height=self.window_height
        self.canvas_width=self.window_width
        
        # make sure focus is set.
        self.root.focus_set()

        #define response to main window closing.
        self.root.protocol ("WM_DELETE_WINDOW", self.on_break_key)

        # Always use CTRL-Break key to close the program as a get out of jail
        self.root.bind("<Break>",self.e_on_break_key)
        
        #pass all other keys along to start shows and hence to 'players'
        self.root.bind("<Escape>", self._escape_pressed)
        self.root.bind("<Up>", self._up_pressed)
        self.root.bind("<Down>", self._down_pressed)
        self.root.bind("<Return>", self._return_pressed)
        self.root.bind("<space>", self._pause_pressed)
        self.root.bind("p", self._pause_pressed)

        #setup a canvas onto which will be drawn the images or text
        self.canvas = Canvas(self.root, bg='black')

        self.canvas.config(height=self.canvas_height, width=self.canvas_width)
        self.canvas.pack()
        # make sure focus is set on canvas.
        self.canvas.focus_set()


# ****************************************
# INITIALISE THE APPLICATION AND START
# ****************************************
        self.shutdown_required=False
        
        #kick off GPIO if enabled by command line option
        if self.options['gpio']==True:
            from pp_gpio import PPIO
            # initialise the GPIO
            self.ppio=PPIO()
            # PPIO.gpio_enabled=False
            if self.ppio.init(pp_dir,self.pp_profile,self.canvas,50,self.button_pressed)==False:
                self._end('error','gpio error')
                
            # and start polling gpio
            self.ppio.poll()

        #kick off the time of day scheduler
        self.tod=TimeOfDay()
        self.tod.init(pp_dir,self.pp_home,self.canvas,500)
        self.tod.poll()

        # Create list of start shows initialise them and then run them
        self.start_shows = self.create_start_show_list()
        self.init_shows()
        self.run_shows()
        self.root.mainloop( )
Ejemplo n.º 26
0
    def __init__(self,
                        show_id,
                         root,
                        canvas,
                        show_params,
                        track_params,
                     pp_dir,
                        pp_home,
                        pp_profile):

        self.mon=Monitor()
        self.mon.on()

        
        #instantiate arguments
        self.show_id=show_id
        self.root=root,
        self.canvas = canvas
        self.show_params=show_params  
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile

        # get duration limit (secs ) from profile
        if self.track_params['duration']<>"":
            self.duration= int(self.track_params['duration'])
        else:
            self.duration= int(self.show_params['duration'])
        self.duration_limit=20*self.duration


        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>"":
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']

        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # could put instance generation in play, not sure which is better.
        self.bplayer=uzblDriver(self.canvas)
        self.command_timer=None
        self.tick_timer=None
        self.init_play_state_machine()
Ejemplo n.º 27
0
class AudioPlayer:
    """       
            plays an audio track using mplayer against a coloured backgroud and image
            track can be paused and interrupted
            See pp_imageplayer for common software design description
    """

    #state constants
    _CLOSED = "mplayer_closed"  #probably will not exist
    _STARTING = "mplayer_starting"  #track is being prepared
    _PLAYING = "mplayer_playing"  #track is playing to the screen, may be paused
    _ENDING = "mplayer_ending"  #track is in the process of ending due to quit or end of track
    _WAITING = "wait for timeout"  # track has finished but timeout still running

    # audio mixer matrix settings
    _LEFT = "channels=2:1:0:0:1:1"
    _RIGHT = "channels=2:1:0:1:1:0"
    _STEREO = "channels=2"

    # ***************************************
    # EXTERNAL COMMANDS
    # ***************************************

    def __init__(self, show_id, root, canvas, show_params, track_params,
                 pp_dir, pp_home, pp_profile):

        self.mon = Monitor()
        self.mon.off()

        #instantiate arguments
        self.show_id = show_id
        self.root = root
        self.canvas = canvas
        self.show_params = show_params
        self.track_params = track_params
        self.pp_dir = pp_dir
        self.pp_home = pp_home
        self.pp_profile = pp_profile

        # get duration limit (secs ) from profile
        if self.track_params['duration'] <> '':
            self.duration = int(self.track_params['duration'])
            self.duration_limit = 20 * self.duration
        else:
            self.duration_limit = -1

        # get background image from profile.
        self.background_file = ''
        if self.track_params['background-image'] <> "":
            self.background_file = self.track_params['background-image']
        else:
            if self.track_params['display-show-background'] == 'yes':
                self.background_file = self.show_params['background-image']

        # get background colour from profile.
        if self.track_params['background-colour'] <> "":
            self.background_colour = self.track_params['background-colour']
        else:
            self.background_colour = self.show_params['background-colour']

        # get audio device from profile.
        if self.track_params['mplayer-audio'] <> "":
            self.mplayer_audio = self.track_params['mplayer-audio']
        else:
            self.mplayer_audio = self.show_params['mplayer-audio']

        # get audio volume from profile.
        if self.track_params['mplayer-volume'] <> "":
            self.mplayer_volume = self.track_params['mplayer-volume'].strip()
        else:
            self.mplayer_volume = self.show_params['mplayer-volume'].strip()
        self.volume_option = 'volume=' + self.mplayer_volume

        #get speaker from profile
        if self.track_params['audio-speaker'] <> "":
            self.audio_speaker = self.track_params['audio-speaker']
        else:
            self.audio_speaker = self.show_params['audio-speaker']

        if self.audio_speaker == 'left':
            self.speaker_option = AudioPlayer._LEFT
        elif self.audio_speaker == 'right':
            self.speaker_option = AudioPlayer._RIGHT
        else:
            self.speaker_option = AudioPlayer._STEREO

        #get animation instructions from profile
        self.animate_begin_text = self.track_params['animate-begin']
        self.animate_end_text = self.track_params['animate-end']

        # open the plugin Manager
        self.pim = PluginManager(self.show_id, self.root, self.canvas,
                                 self.show_params, self.track_params,
                                 self.pp_dir, self.pp_home, self.pp_profile)

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # could put instance generation in play, not sure which is better.
        self.mplayer = mplayerDriver(self.canvas)
        self.tick_timer = None
        self.init_play_state_machine()

    def play(self,
             track,
             showlist,
             end_callback,
             ready_callback,
             enable_menu=False):

        #instantiate arguments
        self.track = track
        self.showlist = showlist
        self.end_callback = end_callback  # callback when finished
        self.ready_callback = ready_callback  #callback when ready to play
        self.enable_menu = enable_menu

        # select the sound device
        if self.mplayer_audio <> "":
            if self.mplayer_audio == 'hdmi':
                os.system("amixer -q -c 0 cset numid=3 2")
            else:
                os.system("amixer -q -c 0 cset numid=3 1")

        # callback to the calling object to e.g remove egg timer.
        if self.ready_callback <> None:
            self.ready_callback()

        # create an  instance of showmanager so we can control concurrent shows
        self.show_manager = ShowManager(self.show_id, self.showlist,
                                        self.show_params, self.root,
                                        self.canvas, self.pp_dir,
                                        self.pp_profile, self.pp_home)

        # Control other shows at beginning
        reason, message = self.show_manager.show_control(
            self.track_params['show-control-begin'])
        if reason == 'error':
            self.mon.err(self, message)
            self.end_callback(reason, message)
            self = None
        else:
            # display image and text
            reason, message = self.display_content()
            if reason == 'error':
                self.mon.err(self, message)
                self.end_callback(reason, message)
                self = None
            else:
                # create animation events
                reason, message = self.ppio.animate(self.animate_begin_text,
                                                    id(self))
                if reason == 'error':
                    self.mon.err(self, message)
                    self.end_callback(reason, message)
                    self = None
                else:
                    # start playing the track.
                    if self.duration_limit <> 0:
                        self.start_play_state_machine()
                    else:
                        self.tick_timer = self.canvas.after(10, self.end_zero)

    def end_zero(self):
        self.end('normal', 'zero duration')

    def terminate(self, reason):
        """
        terminate the  player in special circumstances
        normal user termination if by key_pressed 'stop'
        reason will be killed or error
        """
        # circumvents state machine to terminate lower level and then itself.
        if self.mplayer <> None:
            self.mon.log(self, "sent terminate to mplayerdriver")
            self.mplayer.terminate(reason)
            self.end('killed', ' end without waiting for mplayer to finish'
                     )  # end without waiting
        else:
            self.mon.log(self, "terminate, mplayerdriver not running")
            self.end('killed', 'terminate, mplayerdriver not running')

    def get_links(self):
        return self.track_params['links']

    def input_pressed(self, symbol):
        if symbol[0:6] == 'mplay-':
            self.control(symbol[6])

        elif symbol == 'pause':
            self.pause()

        elif symbol == 'stop':
            self.stop()

# ***************************************
# INTERNAL FUNCTIONS
# ***************************************

#toggle pause

    def pause(self):
        if self.play_state in (AudioPlayer._PLAYING,
                               AudioPlayer._ENDING) and self.track <> '':
            self.mplayer.pause()
            return True
        else:
            self.mon.log(self, "!<pause rejected")
            return False

    # other control when playing, not currently used
    def control(self, char):
        if self.play_state == AudioPlayer._PLAYING and self.track <> '' and char not in (
                'q'):
            self.mon.log(self, "> send control to mplayer: " + char)
            self.mplayer.control(char)
            return True
        else:
            self.mon.log(self, "!<control rejected")
            return False

    # respond to normal stop
    def stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self, ">stop received")
        self.quit_signal = True

# ***************************************
#  sequencing
# ***************************************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the mplayer process is not running, mplayer process can be initiated
         - _starting - mplayer process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - mplayer is doing its termination, controls cannot be sent
    """
    def init_play_state_machine(self):
        self.quit_signal = False
        self.play_state = AudioPlayer._CLOSED

    def start_play_state_machine(self):
        #initialise all the state machine variables
        self.duration_count = 0
        self.quit_signal = False  # signal that user has pressed stop

        #play the track
        options = self.show_params[
            'mplayer-other-options'] + '-af ' + self.speaker_option + ',' + self.volume_option + ' '
        if self.track <> '':
            self.mplayer.play(self.track, options)
            self.mon.log(self,
                         'Playing track from show Id: ' + str(self.show_id))
            self.play_state = AudioPlayer._STARTING
        else:
            self.play_state = AudioPlayer._PLAYING
        # and start polling for state changes and count duration
        self.tick_timer = self.canvas.after(50, self.play_state_machine)

    def play_state_machine(self):
        self.duration_count += 1

        if self.play_state == AudioPlayer._CLOSED:
            self.mon.log(self, "      State machine: " + self.play_state)
            return

        elif self.play_state == AudioPlayer._STARTING:
            self.mon.log(self, "      State machine: " + self.play_state)

            # if mplayer is playing the track change to play state
            if self.mplayer.start_play_signal == True:
                self.mon.log(
                    self,
                    "            <start play signal received from mplayer")
                self.mplayer.start_play_signal = False
                self.play_state = AudioPlayer._PLAYING
                self.mon.log(self,
                             "      State machine: mplayer_playing started")
            self.tick_timer = self.canvas.after(50, self.play_state_machine)

        elif self.play_state == AudioPlayer._PLAYING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # service any queued stop signals
            if self.quit_signal == True or (
                    self.duration_limit > 0
                    and self.duration_count > self.duration_limit):
                self.mon.log(self,
                             "      Service stop required signal or timeout")
                # self.quit_signal=False
                if self.track <> '':
                    self.stop_mplayer()
                    self.play_state = AudioPlayer._ENDING
                else:
                    self.play_state = AudioPlayer._CLOSED
                    self.end('normal', 'stop required signal or timeout')

            # mplayer reports it is terminating so change to ending state
            if self.track <> '' and self.mplayer.end_play_signal:
                self.mon.log(self, "            <end play signal received")
                self.mon.log(
                    self, "            <end detected at: " +
                    str(self.mplayer.audio_position))
                self.play_state = AudioPlayer._ENDING
            self.tick_timer = self.canvas.after(50, self.play_state_machine)

        elif self.play_state == AudioPlayer._ENDING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # if spawned process has closed can change to closed state
            # self.mon.log (self,"      State machine : is mplayer process running? -  "  + str(self.mplayer.is_running()))
            if self.mplayer.is_running() == False:
                self.mon.log(self, "            <mplayer process is dead")
                if self.quit_signal == True:
                    self.quit_signal = False
                    self.play_state = AudioPlayer._CLOSED
                    self.end('normal', 'quit required or timeout')
                elif self.duration_limit > 0 and self.duration_count < self.duration_limit:
                    self.play_state = AudioPlayer._WAITING
                    self.tick_timer = self.canvas.after(
                        50, self.play_state_machine)
                else:
                    self.play_state = AudioPlayer._CLOSED
                    self.end('normal', 'mplayer dead')
            else:
                self.tick_timer = self.canvas.after(50,
                                                    self.play_state_machine)

        elif self.play_state == AudioPlayer._WAITING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            if self.quit_signal == True or (
                    self.duration_limit > 0
                    and self.duration_count > self.duration_limit):
                self.mon.log(
                    self,
                    "      Service stop required signal or timeout from wait")
                self.quit_signal = False
                self.play_state = AudioPlayer._CLOSED
                self.end('normal', 'mplayer dead')
            else:
                self.tick_timer = self.canvas.after(50,
                                                    self.play_state_machine)

    def stop_mplayer(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,
                     "         >stop mplayer received from state machine")
        if self.play_state == AudioPlayer._PLAYING:
            self.mplayer.stop()
            return True
        else:
            self.mon.log(self, "!<stop rejected")
            return False

# *****************
# ending the player
# *****************

    def end(self, reason, message):
        # stop the plugin
        if self.track_params['plugin'] <> '':
            self.pim.stop_plugin()

        # abort the timer
        if self.tick_timer <> None:
            self.canvas.after_cancel(self.tick_timer)
            self.tick_timer = None

        if reason in ('error', 'killed'):
            self.end_callback(reason, message)
            self = None

        else:
            # normal end so do show control and animation

            # Control concurrent shows at end
            reason, message = self.show_manager.show_control(
                self.track_params['show-control-end'])
            if reason == 'error':
                self.mon.err(self, message)
                self.end_callback(reason, message)
                self = None
            else:
                # clear events list for this track
                if self.track_params['animate-clear'] == 'yes':
                    self.ppio.clear_events_list(id(self))

                # create animation events for ending
                reason, message = self.ppio.animate(self.animate_end_text,
                                                    id(self))
                if reason == 'error':
                    self.mon.err(self, message)
                    self.end_callback(reason, message)
                    self = None
                else:
                    self.end_callback('normal', "track has terminated or quit")
                    self = None

# *****************
# displaying things
# *****************

    def display_content(self):

        #if self.background_file<>'' or self.show_params['show-text']<> '' or #self.track_params['track-text']<> '' or self.enable_menu== True or #self.track_params['clear-screen']=='yes':

        if self.track_params['clear-screen'] == 'yes':
            self.canvas.delete('pp-content')
            # self.canvas.update()

        #background colour
        if self.background_colour <> '':
            self.canvas.config(bg=self.background_colour)

        if self.background_file <> '':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(
                    self, "Audio background file not found: " +
                    self.background_img_file)
                self.end('error', "Audio background file not found")
            else:
                pil_background_img = PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(
                    int(self.canvas['width']) / 2,
                    int(self.canvas['height']) / 2,
                    image=self.background,
                    anchor=CENTER,
                    tag='pp-content')

        # execute the plugin if required
        if self.track_params['plugin'] <> '':

            reason, message, self.track = self.pim.do_plugin(
                self.track,
                self.track_params['plugin'],
            )
            if reason <> 'normal':
                return reason, message

        # display hint text if enabled

        if self.enable_menu == True:
            self.canvas.create_text(int(self.show_params['hint-x']),
                                    int(self.show_params['hint-y']),
                                    text=self.show_params['hint-text'],
                                    fill=self.show_params['hint-colour'],
                                    font=self.show_params['hint-font'],
                                    anchor=NW,
                                    tag='pp-content')

        # display show text if enabled
        if self.show_params['show-text'] <> '' and self.track_params[
                'display-show-text'] == 'yes':
            self.canvas.create_text(int(self.show_params['show-text-x']),
                                    int(self.show_params['show-text-y']),
                                    anchor=NW,
                                    text=self.show_params['show-text'],
                                    fill=self.show_params['show-text-colour'],
                                    font=self.show_params['show-text-font'],
                                    tag='pp-content')

        # display track text if enabled
        if self.track_params['track-text'] <> '':
            self.canvas.create_text(
                int(self.track_params['track-text-x']),
                int(self.track_params['track-text-y']),
                anchor=NW,
                text=self.track_params['track-text'],
                fill=self.track_params['track-text-colour'],
                font=self.track_params['track-text-font'],
                tag='pp-content')

        self.mon.log(self, "Displayed background and text ")

        self.canvas.tag_raise('pp-click-area')

        self.canvas.update_idletasks()

        return 'normal', ''

# *****************
# utilities
# *****************

    def complete_path(self, track_file):
        #  complete path of the filename of the selected entry
        if track_file[0] == "+":
            track_file = self.pp_home + track_file[1:]
        self.mon.log(self, "Background image is " + track_file)
        return track_file
    def __init__(self,
                         show_id,
                         root,
                        canvas,
                        show_params,
                        track_params ,
                         pp_dir,
                        pp_home,
                        pp_profile):

        self.mon=Monitor()
        self.mon.on()
        
        #instantiate arguments
        self.show_id=show_id
        self.root=root
        self.canvas = canvas
        self.show_params=show_params
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile


        # get config from medialist if there.
        if self.track_params['omx-audio']<>"":
            self.omx_audio= self.track_params['omx-audio']
        else:
            self.omx_audio= self.show_params['omx-audio']
        if self.omx_audio<>"": self.omx_audio= "-o "+ self.omx_audio
        
        if self.track_params['omx-volume']<>"":
            self.omx_volume= self.track_params['omx-volume']
        else:
            self.omx_volume= self.show_params['omx-volume']
        if self.omx_volume<>"":
            self.omx_volume= "--vol "+ str(int(self.omx_volume)*100) + ' '

        if self.track_params['omx-window']<>'':
            self.omx_window= self.track_params['omx-window']
        else:
            self.omx_window= self.show_params['omx-window']


        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>"":
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']
        
        self.centre_x = int(self.canvas['width'])/2
        self.centre_y = int(self.canvas['height'])/2
        
        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()        
        
        # could put instance generation in play, not sure which is better.
        self.omx=OMXDriver(self.canvas)
        self.tick_timer=None
        self.init_play_state_machine()
Ejemplo n.º 29
0
class ImagePlayer:
    """ Displays an image on a canvas for a period of time. Image display can be interrupted
          Implements animation of transitions but Pi is too slow without GPU aceleration."""

    # slide state constants
    NO_SLIDE = 0
    SLIDE_IN = 1
    SLIDE_DWELL= 2
    SLIDE_OUT= 3

# *******************
# external commands
# *******************

    def __init__(self,show_id,canvas,pp_home,show_params,track_params):
        """
                canvas - the canvas onto which the image is to be drawn
                cd -  dictionary of show parameters
                track_params - disctionary of track paramters
        """

        self.mon=Monitor()
        self.mon.on()

        self.show_id=show_id
        self.canvas=canvas
        self.pp_home=pp_home
        self.show_params=show_params
        self.track_params=track_params

        # open resources
        self.rr=ResourceReader()

            
        # get config from medialist if there.
        
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']
        
        if 'duration' in self.track_params and self.track_params['duration']<>"":
            self.duration= int(self.track_params['duration'])
        else:
            self.duration= int(self.show_params['duration'])
            
        if 'transition' in self.track_params and self.track_params['transition']<>"":
            self.transition= self.track_params['transition']
        else:
            self.transition= self.show_params['transition']
  
        # keep dwell and porch as an integer multiple of tick          
        self.porch = 1000 #length of pre and post porches for an image (milliseconds)
        self.tick = 100 # tick time for image display (milliseconds)
        self.dwell = (1000*self.duration)- (2*self.porch)
        if self.dwell<0: self.dwell=0

        self.centre_x = int(self.canvas['width'])/2
        self.centre_y = int(self.canvas['height'])/2

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

    def play(self,
                    track,
                    end_callback,
                    ready_callback,
                    enable_menu=False,
                    starting_callback=None,
                    playing_callback=None,
                    ending_callback=None):
                        
        # instantiate arguments
        self.track=track
        self.enable_menu=enable_menu
        self.ready_callback=ready_callback
        self.end_callback=end_callback

        #init state and signals
        self.state=ImagePlayer.NO_SLIDE
        self.quit_signal=False
        self.kill_required_signal=False
        self.error=False
        self._tick_timer=None
        self.drawn=None
        self.paused=False
        self.pause_text=None

        if os.path.exists(self.track)==True:
            self.pil_image=PIL.Image.open(self.track)
            # adjust brightness and rotate (experimental)
            # pil_image_enhancer=PIL.ImageEnhance.Brightness(pil_image)
            # pil_image=pil_image_enhancer.enhance(0.1)
            # pil_image=pil_image.rotate(45)
            # tk_image = PIL.ImageTk.PhotoImage(pil_image)
        else:
            self.pil_image=None

        # and start image rendering
        self.mon.log(self,'playing track from show Id: '+str(self.show_id))
        self._start_front_porch()

        
    def key_pressed(self,key_name):
        if key_name=='':
            return
        elif key_name in ('p',' '):
            self.pause()
        elif key_name=='escape':
            self._stop()
            return

    def button_pressed(self,button,edge):
        if button =='pause':
            self.pause()
        elif button=='stop':
            self._stop()
            return

    def terminate(self,reason):
        if reason=='error':
            self.error=True
            self.quit_signal=True
        else:
            self.kill_required_signal=True
            self.quit_signal=True

    def pause(self):
        if not self.paused:
            self.paused = True
        else:
            self.paused=False

    
        
# *******************
# internal functions
# *******************

    def _stop(self):
        self.quit_signal=True
        
    def _error(self):
        self.error=True
        self.quit_signal=True
  
     #called when back porch has completed or quit signal is received
    def _end(self,reason,message):
        if self._tick_timer<>None:
            self.canvas.after_cancel(self._tick_timer)
            self._tick_timer=None
        self.quit_signal=False
        # self.canvas.delete(ALL)
        self.canvas.update_idletasks( )
        self.state=self.NO_SLIDE
        if self.error==True or reason=='error':
            self.end_callback("error",message)
            self=None          
        elif self.kill_required_signal==True:
            self.end_callback("killed",message)
            self=None           
        else:
           # clear events list for this track
            if self.track_params['animate-clear']=='yes':
                self.ppio.clear_events_list(id(self))
            # create animation events for ending
            error_text=self.ppio.animate(self.animate_end_text,id(self))
            if error_text=='':
                self.end_callback('normal',"track has terminated or quit")
                self=None
            else:
                self.mon.err(self,error_text)
                self.end_callback("error",'error')
                self=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._error()
        else:
            return value



    def _start_front_porch(self):
        self.state=ImagePlayer.SLIDE_IN
        self.porch_counter=0
        if self.ready_callback<>None: self.ready_callback()

        if self.pil_image<>None or self.enable_menu== True or self.show_params['show-text']<> '' or self.track_params['track-text']<> '':
                self.canvas.delete(ALL)

        if self.transition=="cut":
            #just display the slide full brightness. No need for porch but used for symmetry
            if self.pil_image<>None:
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                      image=self.tk_img, anchor=CENTER)

  
        elif self.transition=="fade":
            #experimental start black and increase brightness (controlled by porch_counter).
            self._display_image()

        elif self.transition == "slide":
            #experimental, start in middle and move to right (controlled by porch_counter)
            if self.pil_image<>None:
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                  image=self.tk_img, anchor=CENTER)
            
        elif self.transition=="crop":
            #experimental, start in middle and crop from right (controlled by porch_counter)
            if self.pil_image<>None:
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                  image=self.tk_img, anchor=CENTER)
        self.canvas.update_idletasks()

        # create animation events
        error_text=self.ppio.animate(self.animate_begin_text,id(self))
        if error_text<>'':
            self.mon.err(self,error_text)
            self.error=True
            self._end()
                                                  
        self._tick_timer=self.canvas.after(self.tick, self._do_front_porch)
        
            
    def _do_front_porch(self):
        if self.quit_signal == True:
            self._end('normal','user quit')
        else:
            self.porch_counter=self.porch_counter+1
            # print "doing slide front porch " +str(self.porch_counter)
            self.canvas.config(bg='black')
            self._display_image()
            if self.porch_counter==self.porch/self.tick:
                self._start_dwell()
            else:
                self._tick_timer=self.canvas.after(self.tick,self._do_front_porch)


    def _start_dwell(self):
        self.state=ImagePlayer.SLIDE_DWELL
        self.dwell_counter=0
        self._tick_timer=self.canvas.after(self.tick, self._do_dwell)

        
    def _do_dwell(self):
        if self.quit_signal == True:
            self.mon.log(self,"quit received")
            self._end('normal','user quit')
        else:
            if self.paused == False:
                self.dwell_counter=self.dwell_counter+1

            # one time flipping of pause text
            if self.paused==True and self.pause_text==None:
                self.pause_text=self.canvas.create_text(100,100, anchor=NW,
                                                      text=self.resource('imageplayer','m01'),
                                                      fill="white",
                                                      font="arial 25 bold")
                self.canvas.update_idletasks( )
                
            if self.paused==False and self.pause_text<>None:
                    self.canvas.delete(self.pause_text)
                    self.pause_text=None
                    self.canvas.update_idletasks( )

            if self.dwell_counter==self.dwell/self.tick:
                self._start_back_porch()
            else:
                self._tick_timer=self.canvas.after(self.tick, self._do_dwell)

    def _start_back_porch(self):
        self.state=ImagePlayer.SLIDE_OUT
        self.porch_counter=self.porch/self.tick
        
        if self.transition=="cut":
             # just keep displaying the slide full brightness.
            # No need for porch but used for symmetry
             pass
            
        elif self.transition=="fade":
            #experimental start full and decrease brightness (controlled by porch_counter).
            self._display_image()

        elif self.transition== "slide":
            #experimental, start in middle and move to right (controlled by porch_counter)
            if self.pil_image<>None:
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                  image=self.tk_img, anchor=CENTER)
            
        elif self.transition =="crop":
            #experimental, start in middle and crop from right (controlled by porch_counter)
            if self.pil_image<>None:
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                  image=self.tk_img, anchor=CENTER)

        self._tick_timer=self.canvas.after(self.tick, self._do_back_porch)

            
    def _do_back_porch(self):
        if self.quit_signal == True:
            self._end('normal','user quit')
        else:
            self.porch_counter=self.porch_counter-1
            self._display_image()
            if self.porch_counter==0:
                self._end('normal','finished')
            else:
                self._tick_timer=self.canvas.after(self.tick,self._do_back_porch)

    



    def _display_image(self):
        if self.transition=="cut":
            pass
        
        # all the methods below have incorrect code !!!
        elif self.transition=="fade":
            if self.pil_image<>None:
                self.enh=PIL.ImageEnhance.Brightness(self.pil_image)
                prop=float(self.porch_counter)/float(20)  #????????
                self.pil_img=self.enh.enhance(prop)
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_img)
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                      image=self.tk_img, anchor=CENTER)

        elif self.transition=="slide":
            if self.pil_image<>None:
                self.canvas.move(self.drawn,5,0)
            
        elif self.transition=="crop":
            if self.pil_image<>None:
                self.crop= 10*self.porch_counter
                self.pil_img=self.pil_image.crop((0,0,1000-self.crop,1080))
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_img)           
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                      image=self.tk_img, anchor=CENTER)


        # display instructions if enabled
       
        if self.enable_menu== True:
            self.canvas.create_text(self.centre_x, 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'])

        # display show text if enabled
        if self.show_params['show-text']<> '':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'])
            
        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'])
            
        self.canvas.update_idletasks( )