Exemplo n.º 1
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
Exemplo n.º 2
0
class mplayerDriver(object):

    _STATUS_REXP = re.compile(r"V :\s*([\d.]+).*")
    _DONE_REXP = re.compile(r"Exiting*")

    _LAUNCH_CMD = "mplayer  -quiet "

    def __init__(self, widget):

        self.widget = widget

        self.mon = Monitor()
        self.mon.off()
        self._process = None
        self.paused = None

    def control(self, char):
        if self._process <> None:
            self._process.send(char)

    def pause(self):
        if self._process <> None:
            self._process.send("p")
            if not self.paused:
                self.paused = True
            else:
                self.paused = False

    def play(self, track, options):
        self._pp(track, options, False)

    def prepare(self, track, options):
        self._pp(track, options, True)

    def show(self):
        # unpause to start playing
        if self._process <> None:
            self._process.send("p")
            self.paused = False

    def stop(self):
        if self._process <> None:
            self._process.send("q")

    # kill the subprocess (mplayer). Used for tidy up on exit.
    def terminate(self, reason):
        self.terminate_reason = reason
        if self._process <> None:
            self._process.send("q")
        else:
            self.end_play_signal = True

    def terminate_reason(self):
        return self.terminate_reason

    # test of whether _process is running
    def is_running(self):
        return self._process.isalive()

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

    def _pp(self, track, options, pause_before_play):
        self.paused = False
        self.start_play_signal = False
        self.end_play_signal = False
        self.terminate_reason = ""
        track = "'" + track.replace("'", "'\\''") + "'"
        cmd = mplayerDriver._LAUNCH_CMD + options + " " + track
        self.mon.log(self, "Send command to mplayer: " + cmd)
        self._process = pexpect.spawn(cmd)

        # uncomment to monitor output to and input from mplayer (read pexpect manual)
        # fout= file('/home/pi/pipresents/mplayerlogfile.txt','w')  #uncomment and change sys.stdout to fout to log to a file
        # self._process.logfile_send = sys.stdout  # send just commands to stdout
        # self._process.logfile=fout  # send all communications to log file

        if pause_before_play:
            self._process.send("p")
            self.paused = True

        # start the thread that is going to monitor sys.stdout. Presumably needs a thread because of blocking

        self._position_thread = Thread(target=self._get_position)
        self._position_thread.start()

    def _get_position(self):
        # print 'hang'
        # while True:
        # pass
        self.start_play_signal = True

        self.audio_position = 0.0

        while True:
            index = self._process.expect(
                [mplayerDriver._STATUS_REXP, pexpect.TIMEOUT, pexpect.EOF, mplayerDriver._DONE_REXP]
            )
            if index == 1:
                continue  # timeout - it doesn't block so is a thread needed?
            elif index in (2, 3):
                # Exiting
                self.end_play_signal = True
                break
            else:
                # presumably matches _STATUS_REXP so get video position
                # has a bug, position is not displayed for an audio track (mp3). Need to look at another field in the status, but how to extract it
                self.audio_position = 0.0
            sleep(0.05)
Exemplo n.º 3
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
Exemplo n.º 4
0
class mplayerDriver(object):

    _STATUS_REXP = re.compile(r"V :\s*([\d.]+).*")
    _DONE_REXP = re.compile(r"Exiting*")

    _LAUNCH_CMD = 'mplayer  -quiet '

    def __init__(self, widget):

        self.widget = widget

        self.mon = Monitor()
        self.mon.off()
        self._process = None
        self.paused = None

    def control(self, char):
        if self._process <> None:
            self._process.send(char)

    def pause(self):
        if self._process <> None:
            self._process.send('p')
            if not self.paused:
                self.paused = True
            else:
                self.paused = False

    def play(self, track, options):
        self._pp(track, options, False)

    def prepare(self, track, options):
        self._pp(track, options, True)

    def show(self):
        # unpause to start playing
        if self._process <> None:
            self._process.send('p')
            self.paused = False

    def stop(self):
        if self._process <> None:
            self._process.send('q')

    # kill the subprocess (mplayer). Used for tidy up on exit.
    def terminate(self, reason):
        self.terminate_reason = reason
        if self._process <> None:
            self._process.send('q')
        else:
            self.end_play_signal = True

    def terminate_reason(self):
        return self.terminate_reason

# test of whether _process is running

    def is_running(self):
        return self._process.isalive()

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

    def _pp(self, track, options, pause_before_play):
        self.paused = False
        self.start_play_signal = False
        self.end_play_signal = False
        self.terminate_reason = ''
        track = "'" + track.replace("'", "'\\''") + "'"
        cmd = mplayerDriver._LAUNCH_CMD + options + " " + track
        self.mon.log(self, "Send command to mplayer: " + cmd)
        self._process = pexpect.spawn(cmd)

        # uncomment to monitor output to and input from mplayer (read pexpect manual)
        # fout= file('/home/pi/pipresents/mplayerlogfile.txt','w')  #uncomment and change sys.stdout to fout to log to a file
        #self._process.logfile_send = sys.stdout  # send just commands to stdout
        # self._process.logfile=fout  # send all communications to log file

        if pause_before_play:
            self._process.send('p')
            self.paused = True

        #start the thread that is going to monitor sys.stdout. Presumably needs a thread because of blocking

        self._position_thread = Thread(target=self._get_position)
        self._position_thread.start()

    def _get_position(self):
        #print 'hang'
        #while True:
        #pass
        self.start_play_signal = True

        self.audio_position = 0.0

        while True:
            index = self._process.expect([
                mplayerDriver._STATUS_REXP, pexpect.TIMEOUT, pexpect.EOF,
                mplayerDriver._DONE_REXP
            ])
            if index == 1:
                continue  # timeout - it doesn't block so is a thread needed?
            elif index in (2, 3):
                #Exiting
                self.end_play_signal = True
                break
            else:
                # presumably matches _STATUS_REXP so get video position
                # has a bug, position is not displayed for an audio track (mp3). Need to look at another field in the status, but how to extract it
                self.audio_position = 0.0
            sleep(0.05)
Exemplo n.º 5
0
class PluginManager:
    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_dir - path of pipresents 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
        self.plugin = None

    # called by players to execute a plugin
    def do_plugin(self, track_file, plugin_cfg):

        # checks existence of and reads the plugin config file
        plugin_cfg_file = self.complete_path(plugin_cfg)
        if not os.path.exists(plugin_cfg_file):
            return "error", "plugin configuration file not found " + plugin_cfg_file, ""
        plugin_params = self.read(plugin_cfg_file)

        # checks the plugin exists
        plugin_dir = self.pp_dir + os.sep + "pp_home" + os.sep + "pp_plugins"
        plugin_file = plugin_dir + os.sep + self.plugin_params["plugin"] + ".py"

        if not os.path.exists(plugin_file):
            return "error", "plugin file not found " + plugin_file, ""

        # import and run the plugin
        name = self.plugin_params["plugin"]
        self.load_plugin(name, plugin_dir)
        error, message, used_track = self.plugin.do_plugin(track_file)
        if error <> "normal":
            return error, message, ""
        else:
            return "normal", "", used_track

    # called by players at the end of a track
    def stop_plugin(self):
        if self.plugin <> None:
            self.plugin.stop_plugin()

    # **************************************
    # plugin utilities
    # **********************************

    def load_plugin(self, name, dir):
        fp, pathname, description = imp.find_module(name, [dir])
        plugin_id = imp.load_module(name, fp, pathname, description)
        self.plugin = plugin_id.Plugin(
            self.root,
            self.canvas,
            self.plugin_params,
            self.track_params,
            self.show_params,
            self.pp_dir,
            self.pp_home,
            self.pp_profile,
        )

    # ***********************************
    # plugin configuration file
    # ***********************************

    def read(self, plugin_cfg_file):
        self.plugin_config = ConfigParser.ConfigParser()
        self.plugin_config.read(plugin_cfg_file)
        self.plugin_params = dict(self.plugin_config.items("plugin"))

    # ***********************************
    # 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:]
        return track_file
Exemplo n.º 6
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
Exemplo n.º 7
0
class PluginManager:
    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_dir - path of pipresents 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
        self.plugin = None

    # called by players to execute a plugin
    def do_plugin(self, track_file, plugin_cfg):

        # checks existence of and reads the plugin config file
        plugin_cfg_file = self.complete_path(plugin_cfg)
        if not os.path.exists(plugin_cfg_file):
            return 'error', 'plugin configuration file not found ' + plugin_cfg_file, ''
        plugin_params = self.read(plugin_cfg_file)

        # checks the plugin exists
        plugin_dir = self.pp_dir + os.sep + 'pp_home' + os.sep + 'pp_plugins'
        plugin_file = plugin_dir + os.sep + self.plugin_params['plugin'] + '.py'

        if not os.path.exists(plugin_file):
            return 'error', 'plugin file not found ' + plugin_file, ''

        # import and run the plugin
        name = self.plugin_params['plugin']
        self.load_plugin(name, plugin_dir)
        error, message, used_track = self.plugin.do_plugin(track_file)
        if error <> 'normal':
            return error, message, ''
        else:
            return 'normal', '', used_track

    # called by players at the end of a track
    def stop_plugin(self):
        if self.plugin <> None:
            self.plugin.stop_plugin()

# **************************************
# plugin utilities
# **********************************

    def load_plugin(self, name, dir):
        fp, pathname, description = imp.find_module(name, [dir])
        plugin_id = imp.load_module(name, fp, pathname, description)
        self.plugin = plugin_id.Plugin(self.root, self.canvas,
                                       self.plugin_params, self.track_params,
                                       self.show_params, self.pp_dir,
                                       self.pp_home, self.pp_profile)

# ***********************************
# plugin configuration file
# ***********************************

    def read(self, plugin_cfg_file):
        self.plugin_config = ConfigParser.ConfigParser()
        self.plugin_config.read(plugin_cfg_file)
        self.plugin_params = dict(self.plugin_config.items('plugin'))

# ***********************************
# 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:]
        return track_file