Пример #1
0
class MediaShow:

    # *******************
    # External interface
    # ********************

    def __init__(self, show_params, root, canvas, showlist, pp_dir, pp_home,
                 pp_profile):
        """ canvas - the canvas that the menu is to be written on
            show - the dictionary fo the show to be played
            pp_home - Pi presents data_home directory
            pp_profile - Pi presents profile directory
        """

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

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

        # open resources
        self.rr = ResourceReader()

        # Init variables
        self.player = None
        self.shower = None
        self.poll_for_interval_timer = None
        self.poll_for_continue_timer = None
        self.waiting_for_interval = False
        self.interval_timer = None
        self.duration_timer = None
        self.error = False

        self.interval_timer_signal = False
        self.end_trigger_signal = False
        self.end_mediashow_signal = False
        self.next_track_signal = False
        self.previous_track_signal = False
        self.play_child_signal = False
        self.req_next = 'nil'

        #create and instance of TimeOfDay scheduler so we can add events
        self.tod = TimeOfDay()

        self.state = 'closed'

    def play(self,
             show_id,
             end_callback,
             show_ready_callback,
             top=False,
             command='nil'):
        """ displays the mediashow
              end_callback - function to be called when the menu exits
              ready_callback - callback when menu is ready to display (not used)
              top is True when the show is top level (run from [start])
        """

        #instantiate the arguments
        self.show_id = show_id
        self.end_callback = end_callback
        self.show_ready_callback = show_ready_callback
        self.top = top
        self.command = command
        self.mon.log(
            self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
            ": Starting show")

        # check  data files are available.
        self.media_file = self.pp_profile + "/" + self.show_params['medialist']
        if not os.path.exists(self.media_file):
            self.mon.err(self, "Medialist file not found: " + self.media_file)
            self.end('error', "Medialist file not found")

        #create a medialist for the mediashow and read it.
        self.medialist = MediaList()
        if self.medialist.open_list(self.media_file,
                                    self.showlist.sissue()) == False:
            self.mon.err(self, "Version of medialist different to Pi Presents")
            self.end('error', "Version of medialist different to Pi Presents")

        #get controls for this show if top level
        controlsmanager = ControlsManager()
        if self.top == True:
            self.controls_list = controlsmanager.default_controls()
            # and merge in controls from profile
            self.controls_list = controlsmanager.merge_show_controls(
                self.controls_list, self.show_params['controls'])

        #set up the time of day triggers for the show
        if self.show_params['trigger'] in ('time', 'time-quiet'):
            error_text = self.tod.add_times(self.show_params['trigger-input'],
                                            id(self), self.tod_start_callback,
                                            self.show_params['trigger'])
            if error_text <> '':
                self.mon.err(self, error_text)
                self.end('error', error_text)

        if self.show_params['trigger-end'] == 'time':
            # print self.show_params['trigger-end-time']
            error_text = self.tod.add_times(
                self.show_params['trigger-end-time'], id(self),
                self.tod_end_callback, 'n/a')
            if error_text <> '':
                self.mon.err(self, error_text)
                self.end('error', error_text)

        if self.show_params['trigger-end'] == 'duration':
            error_text = self.calculate_duration(
                self.show_params['trigger-end-time'])
            if error_text <> '':
                self.mon.err(self, error_text)
                self.end('error', error_text)

        self.state = 'closed'
        self.egg_timer = None
        self.wait_for_trigger()

# ********************************
# Respond to external events
# ********************************

#stop received from another concurrent show

    def managed_stop(self):
        # if next lower show is running pass down to stop the show and lower level
        if self.shower <> None:
            self.shower.managed_stop()
        else:
            #stop the show if not at top
            self.end_mediashow_signal = True
            # and if track is runing stop that first
            if self.player <> None:
                self.player.input_pressed('stop')

    # kill or error
    def terminate(self, reason):
        if self.shower <> None:
            self.shower.terminate(reason)
        elif self.player <> None:
            self.player.terminate(reason)
        else:
            self.end(reason,
                     ' terminated with no shower or player to terminate')

# respond to input events

    def input_pressed(self, symbol, edge, source):
        self.mon.log(
            self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
            ": received input: " + symbol)

        #  check symbol against mediashow triggers, triggers can be used at top or lower level
        # and not affected by disable-controls

        if self.state == 'waiting' and self.show_params['trigger'] in (
                'input',
                'input-quiet') and symbol == self.show_params['trigger-input']:
            self.start_show()
        elif self.state == 'playing' and self.show_params[
                'trigger-next'] == 'input' and symbol == self.show_params[
                    'next-input']:
            self.next()

    # internal functions are triggered only when disable-controls is  'no'
        if self.show_params['disable-controls'] == 'yes':
            return

        # if at top convert symbolic name to operation otherwise lower down we have received an operation
        # look through list of standard symbols to find match (symbolic-name, function name) operation =lookup (symbol
        if self.top == True:
            operation = self.lookup_control(symbol, self.controls_list)
        else:
            operation = symbol

        # print 'operation',operation
        self.do_operation(operation, edge, source)

    #service the standard inputs for this show

    def do_operation(self, operation, edge, source):
        if self.shower <> None:
            # if next lower show is running pass down to stop the show and lower level
            self.shower.input_pressed(operation, edge, source)
        else:
            # control this show and its tracks
            # print 'operation',operation
            if operation == 'stop':
                if self.top == False:
                    # not at top so stop the current show
                    self.end_mediashow_signal = True
                    # and if a track is running stop that first
                    if self.player <> None:
                        self.player.input_pressed('stop')
                else:
                    # top = True, just stop track if running
                    if self.player <> None:
                        self.player.input_pressed('stop')

            elif operation in ('up', 'down'):
                #if playing rather than waiting use keys for next or previous
                if operation == 'up' and self.state == 'playing':
                    self.previous()
                else:
                    self.next()

            elif operation == 'play':
                # use 'play' to start child if state=playing or to trigger the show if waiting for trigger
                if self.state == 'playing':
                    if self.show_params['has-child'] == 'yes':
                        self.play_child_signal = True
                        self.child_track_ref = 'pp-child-show'
                        # and stop the current track if its running
                        if self.player <> None:
                            self.player.input_pressed('stop')
                else:
                    if self.state == 'waiting':
                        self.start_show()

            elif operation == 'pause':
                if self.player <> None:
                    self.player.input_pressed(operation)

            #if the operation is omxplayer or mplayer runtime control then pass it to player if running
            elif operation[0:4] == 'omx-' or operation[
                    0:6] == 'mplay-' or operation[0:5] == 'uzbl-':
                if self.player <> None:
                    self.player.input_pressed(operation)

    def lookup_control(self, symbol, controls_list):
        for control in controls_list:
            if symbol == control[0]:
                return control[1]
        return ''

# ***************************
# Show sequencer
# ***************************

    def end_interval_timer(self):
        self.interval_timer_signal = True

    # callback from time of day scheduler
    def tod_start_callback(self):
        if self.state == 'waiting' and self.show_params['trigger'] in (
                'time', 'time-quiet'):
            self.start_show()

    def tod_end_callback(self):
        if self.state == 'playing' and self.show_params['trigger-end'] in (
                'time', 'duration'):
            self.end_trigger_signal = True
            if self.shower <> None:
                self.shower.input_pressed('stop')
            elif self.player <> None:
                self.player.input_pressed('stop')

    def stop(self, message):
        self.end_mediashow_signal = True
        if self.interval_timer <> None:
            self.canvas.after_cancel(self.interval_timer)

    def next(self):
        # stop track if running and set signal
        self.next_track_signal = True
        if self.shower <> None:
            self.shower.input_pressed("stop")
        else:
            if self.player <> None:
                self.player.input_pressed("stop")

    def previous(self):
        self.previous_track_signal = True
        if self.shower <> None:
            self.shower.input_pressed("stop")
        else:
            if self.player <> None:
                self.player.input_pressed("stop")

    # wait for trigger sets the state to waiting so that events can do a start show.
    def wait_for_trigger(self):
        self.state = 'waiting'
        if self.show_ready_callback <> None:
            self.show_ready_callback()

        self.mon.log(
            self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
            ": Waiting for trigger: " + self.show_params['trigger'])

        if self.show_params['trigger'] == "input":
            # blank screen waiting for trigger if auto, otherwise display something
            if self.show_params['progress'] == "manual":
                text = self.resource('mediashow', 'm01')
            else:
                text = self.resource('mediashow', 'm02')
            self.display_message(self.canvas, 'text', text, 0, self.start_show)

        elif self.show_params['trigger'] == "input-quiet":
            # blank screen waiting for trigger
            text = self.resource('mediashow', 'm10')
            self.display_message(self.canvas, 'text', text, 0, self.start_show)
            pass

        elif self.show_params['trigger'] in ('time', 'time-quiet'):
            # show next show notice
            quiet = 3
            # if next show is this one display text
            next_show = self.tod.next_event_time()
            if next_show[quiet] == False:
                if next_show[1] == 'tomorrow':
                    text = self.resource('mediashow', 'm09')
                else:
                    text = self.resource('mediashow', 'm08')
                text = text.replace('%tt', next_show[0])
                self.display_message(self.canvas, 'text', text, 0,
                                     self.start_show)

        elif self.show_params['trigger'] == "start":
            self.start_show()

        else:
            self.mon.err(self,
                         "Unknown trigger: " + self.show_params['trigger'])
            self.end('error', "Unknown trigger type")

    def start_show(self):
        self.state = 'playing'
        self.direction = 'forward'
        # self.canvas.delete(ALL)
        # start interval timer
        if self.show_params['repeat'] == "interval" and self.show_params[
                'repeat-interval'] <> 0:
            self.interval_timer_signal = False
            self.interval_timer = self.canvas.after(
                int(self.show_params['repeat-interval']) * 1000,
                self.end_interval_timer)

        # start duration timer
        if self.show_params['trigger-end'] == 'duration':
            # print 'set alarm ', self.duration
            self.duration_timer = self.canvas.after(self.duration * 1000,
                                                    self.tod_end_callback)

        # and play the first track unless commanded otherwise
        if self.command == 'backward':
            self.medialist.finish()
        else:
            self.medialist.start()
        self.play_selected_track(self.medialist.selected_track())

    def what_next(self):
        self.direction = 'forward'

        # end of show trigger caused by tod
        if self.end_trigger_signal == True:
            self.end_trigger_signal = False
            if self.top == True:
                self.state = 'waiting'
                self.wait_for_trigger()
            else:
                # not at top so stop the show
                self.end('normal', 'sub-show end time trigger')

        # user wants to end, wait for any shows or tracks to have ended then end show
        # probalby will get here with end_m set when player and shower has finished
        elif self.end_mediashow_signal == True:
            if self.player == None and self.shower == None:
                self.end_mediashow_signal = False
                self.end('normal', "show ended by user")

        #returning from a subshow needing to move onward
        elif self.req_next == 'do-next':
            self.req_next = 'nil'
            self.medialist.next(self.show_params['sequence'])
            self.play_selected_track(self.medialist.selected_track())

        #returning from a subshow needing to move backward
        elif self.req_next == 'do-previous':
            self.req_next = 'nil'
            self.direction = 'backward'
            self.medialist.previous(self.show_params['sequence'])
            self.play_selected_track(self.medialist.selected_track())

        # user wants to play child
        elif self.play_child_signal == True:
            self.play_child_signal = False
            index = self.medialist.index_of_track(self.child_track_ref)
            if index >= 0:
                #don't use select the track as need to preserve mediashow sequence.
                child_track = self.medialist.track(index)
                self.display_eggtimer(self.resource('mediashow', 'm07'))
                self.play_selected_track(child_track)
            else:
                self.mon.err(
                    self, "Child show not found in medialist: " +
                    self.show_params['pp-child-show'])
                self.end('error', "child show not found in medialist")

        # skip to next track on user input
        elif self.next_track_signal == True:
            self.next_track_signal = False
            if self.medialist.at_end() == True:
                if self.show_params[
                        'sequence'] == "ordered" and self.show_params[
                            'repeat'] == 'oneshot' and self.top == False:
                    self.end('do-next', "Return from Sub Show")
                elif self.show_params[
                        'sequence'] == "ordered" and self.show_params[
                            'repeat'] == 'single-run' and self.top == False:
                    self.end('do-next', "Return from Sub Show")
                else:
                    self.medialist.next(self.show_params['sequence'])
                    self.play_selected_track(self.medialist.selected_track())
            else:
                self.medialist.next(self.show_params['sequence'])
                self.play_selected_track(self.medialist.selected_track())

        # skip to previous track on user input
        elif self.previous_track_signal == True:
            self.previous_track_signal = False
            self.direction = 'backward'
            if self.medialist.at_start() == True:
                if self.show_params[
                        'sequence'] == "ordered" and self.show_params[
                            'repeat'] == 'oneshot' and self.top == False:
                    self.end('do-previous', "Return from Sub Show")
                elif self.show_params[
                        'sequence'] == "ordered" and self.show_params[
                            'repeat'] == 'single-run' and self.top == False:
                    self.end('do-previous', "Return from Sub Show")
                else:
                    self.medialist.previous(self.show_params['sequence'])
                    self.play_selected_track(self.medialist.selected_track())
            else:
                self.medialist.previous(self.show_params['sequence'])
                self.play_selected_track(self.medialist.selected_track())

        # track is finished and we are on auto
        elif self.show_params['progress'] == "auto":

            if self.medialist.at_end() == True:

                # oneshot
                if self.show_params[
                        'sequence'] == "ordered" and self.show_params[
                            'repeat'] == 'oneshot' and self.top == False:
                    self.end('normal', "End of Oneshot in subshow")

                elif self.show_params[
                        'sequence'] == "ordered" and self.show_params[
                            'repeat'] == 'oneshot' and self.top == True:
                    self.wait_for_trigger()

                # single run
                elif self.show_params[
                        'sequence'] == "ordered" and self.show_params[
                            'repeat'] == 'single-run' and self.top == True:
                    self.end('normal', "End of Single Run")

                elif self.show_params[
                        'sequence'] == "ordered" and self.show_params[
                            'repeat'] == 'single-run' and self.top == False:
                    self.end('do-next',
                             "End of single run - Return from Sub Show")

                # repeating and waiting to restart
                elif self.waiting_for_interval == True:
                    if self.interval_timer_signal == True:
                        self.interval_timer_signal = False
                        self.waiting_for_interval = False
                        self.start_show()
                    else:
                        self.poll_for_interval_timer = self.canvas.after(
                            1000, self.what_next)

                elif self.show_params[
                        'sequence'] == "ordered" and self.show_params[
                            'repeat'] == 'interval' and int(
                                self.show_params['repeat-interval']) > 0:
                    self.waiting_for_interval = True
                    self.poll_for_interval_timer = self.canvas.after(
                        1000, self.what_next)

                #elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])==0:
                elif self.show_params['repeat'] == 'interval' and int(
                        self.show_params['repeat-interval']) == 0:
                    self.medialist.next(self.show_params['sequence'])
                    self.play_selected_track(self.medialist.selected_track())

                # shuffling so there is no end condition
                elif self.show_params['sequence'] == "shuffle":
                    self.medialist.next(self.show_params['sequence'])
                    self.play_selected_track(self.medialist.selected_track())

                else:
                    self.mon.err(
                        self, "Unhandled playing event: " +
                        self.show_params['sequence'] + ' with ' +
                        self.show_params['repeat'] + " of " +
                        self.show_params['repeat-interval'])
                    self.end('error', "Unhandled playing event")

            else:
                self.medialist.next(self.show_params['sequence'])
                self.play_selected_track(self.medialist.selected_track())

        # track has finished and we are on manual progress
        elif self.show_params['progress'] == "manual":
            self.delete_eggtimer()
            self.canvas.delete('pp-content')
            if self.show_params['trigger-next'] == 'input':
                self.display_eggtimer(self.resource('mediashow', 'm03'))
            self.poll_for_continue_timer = self.canvas.after(
                2000, self.what_next)

        else:
            #unhandled state
            self.mon.err(self, "Unhandled playing event: ")
            self.end('error', "Unhandled playing event")

# ***************************
# Dispatching to Players/Shows
# ***************************

    def ready_callback(self):
        self.delete_eggtimer()

    def play_selected_track(self, selected_track):
        """ selects the appropriate player from type field of the medialist and computes
              the parameters for that type
              selected track is a dictionary for the track/show
        """
        self.delete_eggtimer()
        if self.show_params['progress'] == "manual":
            self.display_eggtimer(self.resource('mediashow', 'm04'))

        # is menu required
        if self.show_params['has-child'] == "yes":
            self.enable_child = True
        else:
            self.enable_child = False

        #dispatch track by type
        self.player = None
        self.shower = None
        track_type = selected_track['type']
        self.mon.log(
            self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
            ": Track type is: " + track_type)
        if track_type == "video":
            # create a videoplayer
            track_file = self.complete_path(selected_track)
            self.player = VideoPlayer(self.show_id, self.root, self.canvas,
                                      self.show_params, selected_track,
                                      self.pp_dir, self.pp_home,
                                      self.pp_profile)
            self.player.play(track_file,
                             self.showlist,
                             self.end_player,
                             self.ready_callback,
                             enable_menu=self.enable_child)

        elif track_type == "audio":
            # create a audioplayer
            track_file = self.complete_path(selected_track)
            self.player = AudioPlayer(self.show_id, self.root, self.canvas,
                                      self.show_params, selected_track,
                                      self.pp_dir, self.pp_home,
                                      self.pp_profile)
            self.player.play(track_file,
                             self.showlist,
                             self.end_player,
                             self.ready_callback,
                             enable_menu=self.enable_child)

        elif track_type == "web":
            # create a browser
            track_file = self.complete_path(selected_track)
            self.player = BrowserPlayer(self.show_id, self.root, self.canvas,
                                        self.show_params, selected_track,
                                        self.pp_dir, self.pp_home,
                                        self.pp_profile)
            self.player.play(track_file,
                             self.showlist,
                             self.end_player,
                             self.ready_callback,
                             enable_menu=self.enable_child)

        elif track_type == "image":
            track_file = self.complete_path(selected_track)
            # images played from menus don't have children
            self.player = ImagePlayer(self.show_id, self.root, self.canvas,
                                      self.show_params, selected_track,
                                      self.pp_dir, self.pp_home,
                                      self.pp_profile)
            self.player.play(track_file,
                             self.showlist,
                             self.end_player,
                             self.ready_callback,
                             enable_menu=self.enable_child)

        elif track_type == "message":
            # bit odd because MessagePlayer is used internally to display text.
            text = selected_track['text']
            self.player = MessagePlayer(self.show_id, self.root, self.canvas,
                                        self.show_params, selected_track,
                                        self.pp_dir, self.pp_home,
                                        self.pp_profile)
            self.player.play(text,
                             self.showlist,
                             self.end_player,
                             self.ready_callback,
                             enable_menu=self.enable_child)

        elif track_type == "show":
            # get the show from the showlist
            index = self.showlist.index_of_show(selected_track['sub-show'])
            if index >= 0:
                self.showlist.select(index)
                selected_show = self.showlist.selected_show()
            else:
                self.mon.err(
                    self, "Show not found in showlist: " +
                    selected_track['sub-show'])
                self.end('error', "Unknown show")

            if selected_show['type'] == "mediashow":
                self.shower = MediaShow(selected_show, self.root, self.canvas,
                                        self.showlist, self.pp_dir,
                                        self.pp_home, self.pp_profile)
                self.shower.play(self.show_id,
                                 self.end_shower,
                                 self.ready_callback,
                                 top=False,
                                 command=self.direction)

            elif selected_show['type'] == "liveshow":
                self.shower = LiveShow(selected_show, self.root, self.canvas,
                                       self.showlist, self.pp_dir,
                                       self.pp_home, self.pp_profile)
                self.shower.play(self.show_id,
                                 self.end_shower,
                                 self.ready_callback,
                                 top=False,
                                 command='nil')

            elif selected_show['type'] == "radiobuttonshow":
                self.shower = RadioButtonShow(selected_show, self.root,
                                              self.canvas, self.showlist,
                                              self.pp_dir, self.pp_home,
                                              self.pp_profile)
                self.shower.play(self.show_id,
                                 self.end_shower,
                                 self.ready_callback,
                                 top=False,
                                 command='nil')

            elif selected_show['type'] == "hyperlinkshow":
                self.shower = HyperlinkShow(selected_show, self.root,
                                            self.canvas, self.showlist,
                                            self.pp_dir, self.pp_home,
                                            self.pp_profile)
                self.shower.play(self.show_id,
                                 self.end_shower,
                                 self.ready_callback,
                                 top=False,
                                 command='nil')

            elif selected_show['type'] == "menu":
                self.shower = MenuShow(selected_show, self.root, self.canvas,
                                       self.showlist, self.pp_dir,
                                       self.pp_home, self.pp_profile)
                self.shower.play(self.show_id,
                                 self.end_shower,
                                 self.ready_callback,
                                 top=False,
                                 command='nil')

            else:
                self.mon.err(self,
                             "Unknown Show Type: " + selected_show['type'])
                self.end('error' "Unknown show type")

        else:
            self.mon.err(self, "Unknown Track Type: " + track_type)
            self.end('error', "Unknown track type")

    def end_player(self, reason, message):
        self.mon.log(
            self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
            ": Returned from player with message: " + message)
        self.player = None
        self.req_next = 'nil'
        if reason in ("killed", "error"):
            self.end(reason, message)
        else:
            # elif>else move to what-next?
            if self.show_params['progress'] == "manual":
                self.display_eggtimer(self.resource('mediashow', 'm05'))
                self.req_next = reason
                self.what_next()
            else:
                self.req_next = reason
                self.what_next()

    def end_shower(self, show_id, reason, message):
        self.mon.log(
            self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
            ": Returned from shower with message: " + message)
        self.shower = None
        self.req_next = 'nil'
        if reason in ("killed", "error"):
            self.end(reason, message)
        else:
            if self.show_params['progress'] == "manual":
                self.display_eggtimer(self.resource('mediashow', 'm06'))
                self.req_next = reason
                self.what_next()
            else:
                self.req_next = reason
                self.what_next()

# ***************************
# end of show
# ***************************

    def end(self, reason, message):
        self.end_mediashow_signal = False
        self.mon.log(
            self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
            ": Ending Mediashow")
        self.tidy_up()
        self.end_callback(self.show_id, reason, message)
        self = None
        return

    def tidy_up(self):
        #clear outstanding time of day events for this show
        # self.tod.clear_times_list(id(self))
        if self.poll_for_continue_timer <> None:
            self.canvas.after_cancel(self.poll_for_continue_timer)
            self.poll_for_continue_timer = None
        if self.poll_for_interval_timer <> None:
            self.canvas.after_cancel(self.poll_for_interval_timer)
            self.poll_for_interval_timer = None
        if self.interval_timer <> None:
            self.canvas.after_cancel(self.interval_timer)
            self.interval_timer = None
        if self.duration_timer <> None:
            self.canvas.after_cancel(self.duration_timer)
            self.duration_timer = None

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

    def display_eggtimer(self, text):
        self.canvas.create_text(int(self.canvas['width']) / 2,
                                int(self.canvas['height']) / 2,
                                text=text,
                                fill='white',
                                font="Helvetica 20 bold",
                                tag='pp-eggtimer')
        self.canvas.update_idletasks()

    def delete_eggtimer(self):
        self.canvas.delete('pp-eggtimer')
        self.canvas.update_idletasks()

    # used to display internal messages in situations where a medialist entry could not be used.
    def display_message(self, canvas, source, content, duration,
                        _display_message_callback):
        self.display_message_callback = _display_message_callback
        tp = {
            'duration': duration,
            'message-colour': 'white',
            'message-font': 'Helvetica 20 bold',
            'background-colour': '',
            'message-justify': 'left',
            'background-image': '',
            'show-control-begin': '',
            'show-control-end': '',
            'animate-begin': '',
            'animate-clear': '',
            'animate-end': '',
            'message-x': '',
            'message-y': '',
            'display-show-background': 'no',
            'display-show-text': 'no',
            'show-text': '',
            'track-text': '',
            'plugin': ''
        }
        self.player = MessagePlayer(self.show_id, self.root, canvas, tp, tp,
                                    self.pp_dir, self.pp_home, self.pp_profile)
        self.player.play(content, self.showlist, self.display_message_end,
                         None, False)

    def display_message_end(self, reason, message):
        self.player = None
        if reason in ('error', 'killed'):
            self.end(reason, message)
        else:
            self.display_message_callback()


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

    def calculate_duration(self, line):
        fields = line.split(':')
        if len(fields) == 1:
            secs = fields[0]
            minutes = '0'
            hours = '0'
        if len(fields) == 2:
            secs = fields[1]
            minutes = fields[0]
            hours = '0'
        if len(fields) == 3:
            secs = fields[2]
            minutes = fields[1]
            hours = fields[0]
        self.duration = 3600 * long(hours) + 60 * long(minutes) + long(secs)
        return ''

    def resource(self, section, item):
        value = self.rr.get(section, item)
        if value == False:
            self.mon.err(self,
                         "resource: " + section + ': ' + item + " not found")
            self.terminate("error")
        else:
            return value

    def complete_path(self, selected_track):
        #  complete path of the filename of the selected entry
        track_file = selected_track['location']
        if track_file <> '' and track_file[0] == "+":
            track_file = self.pp_home + track_file[1:]
        self.mon.log(
            self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
            ": Track to play is: " + track_file)
        return track_file
Пример #2
0
class MediaShow:


# *******************
# External interface
# ********************

    def __init__(self,
                            show,
                            canvas,
                            showlist,
                            pp_home,
                            pp_profile):
        """ canvas - the canvas that the menu is to be written on
            show - the dictionary fo the show to be played
            pp_home - Pi presents data_home directory
            pp_profile - Pi presents profile directory
        """

        self.mon=Monitor()
        self.mon.on()
        
        #instantiate arguments
        self.show =show
        self.showlist=showlist
        self.canvas=canvas
        self.pp_home=pp_home
        self.pp_profile=pp_profile

        # open resources
        self.rr=ResourceReader()

        # Init variables
        self.player=None
        self.shower=None
        self._poll_for_interval_timer=None
        self._poll_for_continue_timer=None
        self._waiting_for_interval=False
        self._interval_timer=None
        self.error=False
        
        self._interval_timer_signal=False
        self._end_mediashow_signal=False
        self._next_track_signal=False
        self._previous_track_signal=False
        self._play_child_signal = False
        self._req_next='nil'

        self._state='closed'


    def play(self,end_callback,ready_callback=None, top=False,command='nil'):

        """ displays the mediashow
              end_callback - function to be called when the menu exits
              ready_callback - callback when menu is ready to display (not used)
              top is True when the show is top level (run from [start])
        """

        #instantiate the arguments
        self._end_callback=end_callback
        self._ready_callback=ready_callback
        self.top=top
        self.command=command
        self.mon.log(self,"Starting show: " + self.show['show-ref'])

        # check  data files are available.
        self.media_file = self.pp_profile + "/" + self.show['medialist']
        if not os.path.exists(self.media_file):
            self.mon.err(self,"Medialist file not found: "+ self.media_file)
            self._end('error',"Medialist file not found")


        #create a medialist for the mediashow and read it.
        self.medialist=MediaList()
        if self.medialist.open_list(self.media_file,self.showlist.sissue())==False:
            self.mon.err(self,"Version of medialist different to Pi Presents")
            self._end('error',"Version of medialist different to Pi Presents")

        self._wait_for_trigger()


   # respond to key presses.
    def key_pressed(self,key_name):
        self.mon.log(self,"received key: " + key_name)
        
        if key_name=='':
            pass
        
        elif key_name=='escape':
            # if next lower show is running pass down to stop the show and lower level
            if self.shower<>None:
                self.shower.key_pressed(key_name)
            # if not at top stop the show
            else:
                if self.top == False:
                    self._end_mediashow_signal=True
                    # and if a track is running stop that first
                    if self.player<>None:
                        self.player.key_pressed(key_name)
                else:
                    # at top level in a manual presentation stop the track
                    if self.show['progress']=='manual':
                        if self.player<>None:
                            self.player.key_pressed(key_name)
    
        elif key_name in ('up','down'):
        # if child or sub-show is running and is a show pass to show, track does not use up/down
        # otherwise use keys for next or previous
            if self.shower<>None:
                self.shower.key_pressed(key_name)
            else:
                if key_name=='up':
                    self._previous()
                else:
                    self._next()
                
        elif key_name=='return':
            # if child show or sub-show is running and is show - pass down- player does not use return
            # ELSE use Return to start child or to start the show if waiting
            if self.shower<>None:
                self.shower.key_pressed(key_name)
            else:
                if self._state=='playing':
                    if self.show['has-child']=='yes':
                        self._play_child_signal=True
                        # and stop the current track if its running
                        if self.player<>None:
                            self.player.key_pressed("escape")
                else:
                    self._start_show()

        elif key_name=='pir':
                self._start_show()
                
        elif key_name in ('p',' '):
            # pass down if show or track running.
            if self.shower<>None:
                self.shower.key_pressed(key_name)
            elif self.player<>None:
                self.player.key_pressed(key_name)
 

    def button_pressed(self,button,edge):
        if button=='play': self.key_pressed("return")
        elif  button =='up': self.key_pressed("up")
        elif button=='down': self.key_pressed("down")
        elif button=='stop': self.key_pressed("escape")
        elif button=='pause': self.key_pressed('p')
        elif button=='PIR': self.key_pressed('pir')


    # kill or error
    def terminate(self,reason):
        if self.shower<>None:
            self.mon.log(self,"sent terminate to shower")
            self.shower.terminate(reason)
        elif self.player<>None:
            self.mon.log(self,"sent terminate to player")
            self.player.terminate(reason)
        else:
            self._end(reason,'terminated without terminating shower or player')

 
    def _tidy_up(self):
        if self._poll_for_continue_timer<>None:
                self.canvas.after_cancel(self._poll_for_continue_timer)
                self._poll_for_continue_timer=None
        if self._poll_for_interval_timer<>None:
                self.canvas.after_cancel(self._poll_for_interval_timer)
                self._poll_for_interval_timer=None
        if self._interval_timer<>None:
            self.canvas.after_cancel(self._interval_timer)
            self._interval_timer=None

    def resource(self,section,item):
        value=self.rr.get(section,item)
        if value==False:
            self.mon.err(self, "resource: "+section +': '+ item + " not found" )
            self.terminate("error")
        else:
            return value

# ***************************
# Respond to key/button presses
# ***************************

    def _stop(self,message):
        self._end_mediashow_signal=True
        if self._interval_timer<>None:
            self.canvas.after_cancel(self._interval_timer)

   
    def _next(self):
        # stop track if running and set signal
        self._next_track_signal=True
        if self.shower<>None:
            self.shower.key_pressed("escape")
        else:
            if self.player<>None:
                self.player.key_pressed("escape")

    def _previous(self):
        self._previous_track_signal=True
        if self.shower<>None:
            self.shower.key_pressed("escape")
        else:
            if self.player<>None:
                self.player.key_pressed("escape")
     
        
# ***************************
# end of show functions
# ***************************

    def _end(self,reason,message):
        self._end_mediashow_signal=False
        self.mon.log(self,"Ending Mediashow: "+ self.show['show-ref'])
        self._tidy_up()
        self._end_callback(reason,message)
        self=None
        return
        



# ***************************
# Show sequencer
# ***************************
 
    def _wait_for_trigger(self):
        self._state='waiting'
        if self.ready_callback<>None:
            self.ready_callback()

        self.mon.log(self,"Waiting for trigger: "+ self.show['trigger'])
        
        if self.show['trigger']=="button":
            # blank screen waiting for trigger if auto, otherwise display something
            if self.show['progress']=="manual":
                text= self.resource('mediashow','m01')
            else:
                text=""
            self.display_message(self.canvas,'text',text,0,self._start_show)


        elif self.show['trigger']=="PIR":
            # blank screen waiting for trigger
            text = self.resource('mediashow','m02')
            self.display_message(self.canvas,'text',text,0,self._start_show)      
            
        elif self.show['trigger']=="start":
            self._start_show()
            
        else:
            self.mon.err(self,"Unknown trigger: "+ self.show['trigger'])
            self._end('error',"Unknown trigger type")
  
        

    def _start_show(self):
        self._state='playing'
        self._direction='forward'
        # start interval timer
        if self.show['repeat']=="interval" and self.show['repeat-interval']<>0:
            self._interval_timer_signal=False
            self._interval_timer=self.canvas.after(int(self.show['repeat-interval'])*1000,self._end_interval_timer)
        # and play the first track unless commanded otherwise
        if self.command=='backward':
            self.medialist.finish()
        else:
            self.medialist.start()
        self._play_selected_track(self.medialist.selected_track())
 
 
    def _what_next(self):
        self._direction='forward'
        
        # user wants to end, wait for any shows or tracks to have ended then end show
        if self._end_mediashow_signal==True:
            if self.player==None and self.shower==None:
                self._end_mediashow_signal=False
                self._end('normal',"show ended by user")
            else:
                pass
            
        #returning from a subshow needing to move onward 
        elif self._req_next=='do-next':
            self._req_next='nil'
            self.medialist.next()
            self._play_selected_track(self.medialist.selected_track())
            
        #returning from a subshow needing to move backward 
        elif self._req_next=='do-previous':
            self._req_next='nil'
            self._direction='backward'
            self.medialist.previous()
            self._play_selected_track(self.medialist.selected_track())         
               
        # user wants to play child
        elif self._play_child_signal == True:
            self._play_child_signal=False
            index = self.medialist.index_of_track('pp-child-show')
            if index >=0:
                #don't select the track as need to preserve mediashow sequence.
                child_track=self.medialist.track(index)
                self._display_eggtimer(self.resource('mediashow','m07'))
                self._play_selected_track(child_track)
            else:
                self.mon.err(self,"Child show not found in medialist: "+ self.show['pp-child-show'])
                self._end('error',"child show not found in medialist")
        
        # skip to next track on user input
        elif self._next_track_signal==True:
            self._next_track_signal=False
            if self.medialist.at_end()==True:
                if  self.show['sequence']=="ordered" and self.show['repeat']=='oneshot' and self.top==False:
                    self._end('do-next',"Return from Sub Show")
                else:
                    self.medialist.next()
                    self._play_selected_track(self.medialist.selected_track())               
            else:
                self.medialist.next()
                self._play_selected_track(self.medialist.selected_track())
                
        # skip to previous track on user input
        elif self._previous_track_signal==True:
            self._previous_track_signal=False
            self._direction='backward'
            if self.medialist.at_start()==True:
                if  self.show['sequence']=="ordered" and self.show['repeat']=='oneshot' and self.top==False:
                    self._end('do-previous',"Return from Sub Show")
                else:
                    self.medialist.previous()
                    self._play_selected_track(self.medialist.selected_track())               
            else:
                self.medialist.previous()              
                self._play_selected_track(self.medialist.selected_track())
        

        # track is finished and we are on auto        
        elif self.show['progress']=="auto":
            if self.medialist.at_end()==True:
                if self.show['sequence']=="ordered" and self.show['repeat']=='oneshot' and self.top==False:
                    self._end('do-next',"Return from Sub Show")
                    
                #### elif    
                elif self.show['sequence']=="ordered" and self.show['repeat']=='oneshot' and self.top==True:
                    self._wait_for_trigger()

                elif self._waiting_for_interval==True:
                    if self._interval_timer_signal==True:
                        self._interval_timer_signal=False
                        self._waiting_for_interval=False
                        self._start_show()
                    else:
                        self._poll_for_interval_timer=self.canvas.after(1000,self._what_next)
 
                elif self.show['sequence']=="ordered" and self.show['repeat']=='interval' and int(self.show['repeat-interval'])>0:
                    self._waiting_for_interval=True
                    self._poll_for_interval_timer=self.canvas.after(1000,self._what_next) 
                    
                elif self.show['sequence']=="ordered" and self.show['repeat']=='interval' and int(self.show['repeat-interval'])==0:
                    self.medialist.next()
                    self._play_selected_track(self.medialist.selected_track())
                           
                else:
                    self.mon.err(self,"Unhandled playing event: ")
                    self._end('error',"Unhandled playing event")
                    
            else:
                self.medialist.next()
                self._play_selected_track(self.medialist.selected_track())
                    
        # track has finished and we are on manual progress               
        elif self.show['progress']=="manual":
                    self._delete_eggtimer()
                    self._display_eggtimer(self.resource('mediashow','m03'))
                    self._poll_for_continue_timer=self.canvas.after(500,self._what_next)
                    
        else:
            #unhandled state
            self.mon.err(self,"Unhandled playing event: ")
            self._end('error',"Unhandled playing event")           



    def _end_interval_timer(self):
        self._interval_timer_signal=True
 

        

# ***************************
# Dispatching to Players/Shows 
# ***************************

    # used to display internal messages in situations where a medialist entry could be used.
    def display_message(self,canvas,source,content,duration,_display_message_callback):
            self._display_message_callback=_display_message_callback
            tp={'duration':duration,'message-colour':'white','message-font':'Helvetica 20 bold'}
            self.player=MessagePlayer(canvas,tp,tp)
            self.player.play(content,self._display_message_end,None)

    def   _display_message_end(self,reason,message):
        self.player=None
        if reason in ('error','killed'):
            self._end(reason,message)
        else:
            self._display_message_callback()


    def complete_path(self,selected_track):
        #  complete path of the filename of the selected entry
        track_file = selected_track['location']
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Track to play is: "+ track_file)
        return track_file     
         
        


    def _play_selected_track(self,selected_track):
        """ selects the appropriate player from type field of the medialist and computes
              the parameters for that type
              selected track is a dictionary for the track/show
        """
        self.canvas.delete(ALL)
        if self.show['progress']=="manual":
            self._display_eggtimer(self.resource('mediashow','m04'))

        # is menu required
        if self.show['has-child']=="yes":
            enable_child=True
        else:
            enable_child=False

        #dispatch track by type
        self.player=None
        self.shower=None
        track_type = selected_track['type']
        self.mon.log(self,"Track type is: "+ track_type)
        
        if track_type=="video":
            # create a videoplayer
            track_file=self.complete_path(selected_track)
            self.player=VideoPlayer(self.canvas,self.show,selected_track)
            self.player.play(track_file,
                                        self.end_player,
                                        self.ready_callback,
                                        enable_menu=enable_child)
                                        
        elif track_type=="image":
            track_file=self.complete_path(selected_track)
            # images played from menus don't have children
            self.player=ImagePlayer(self.canvas,self.show,selected_track)
            self.player.play(track_file,
                                    self.end_player,
                                    self.ready_callback,
                                    enable_menu=enable_child)
                                    
        elif track_type=="message":
            # bit odd because MessagePlayer is used internally to display text. 
            text=selected_track['text']
            self.player=MessagePlayer(self.canvas,self.show,selected_track)
            self.player.play(text,
                                    self.end_player,
                                    self.ready_callback,
                                    enable_menu=enable_child
                                    )
         
 
        elif track_type=="show":
            # get the show from the showlist
            index = self.showlist.index_of_show(selected_track['sub-show'])
            if index >=0:
                self.showlist.select(index)
                selected_show=self.showlist.selected_show()
            else:
                self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show'])
                self._end('error',"Unknown show")
                
            if selected_show['type']=="mediashow":    
                self.shower= MediaShow(selected_show,
                                                                self.canvas,
                                                                self.showlist,
                                                                self.pp_home,
                                                                self.pp_profile)
                self.shower.play(self.end_shower,top=False,command=self._direction)

            elif selected_show['type']=="liveshow":    
                self.shower= LiveShow(selected_show,
                                                                self.canvas,
                                                                self.showlist,
                                                                self.pp_home,
                                                                self.pp_profile)
                self.shower.play(self.end_shower,top=False,command='nil')
            
            elif selected_show['type']=="menu":
                self.shower= MenuShow(selected_show,
                                                        self.canvas,
                                                        self.showlist,
                                                        self.pp_home,
                                                        self.pp_profile)
                self.shower.play(self.end_shower,top=False,command='nil')
                
            else:
                self.mon.err(self,"Unknown Show Type: "+ selected_show['type'])
                self._end('error'"Unknown show type")  
            
        else:
            self.mon.err(self,"Unknown Track Type: "+ track_type)
            self._end('error',"Unknown track type")            


    def ready_callback(self):
        self._delete_eggtimer()
        
        
    def end_player(self,reason,message):
        self._req_next='nil'
        self.mon.log(self,"Returned from player with message: "+ message)
        self.player=None
        if reason in("killed","error"):
            self._end(reason,message)
        elif self.show['progress']=="manual":
            self._display_eggtimer(self.resource('mediashow','m05'))
            self._req_next=reason
            self._what_next()
        else:
            self._req_next=reason
            self._what_next()

    def end_shower(self,reason,message):
        self._req_next='nil'
        self.mon.log(self,"Returned from shower with message: "+ message)
        self.shower=None
        if reason in("killed","error"):
            self._end(reason,message)
        elif self.show['progress']=="manual":
            self._display_eggtimer(self.resource('mediashow','m06'))
            self._req_next=reason
            self._what_next() 
        else:
            self._req_next=reason
            self._what_next() 
        
        
    def _display_eggtimer(self,text):
        self.canvas.create_text(int(self.canvas['width'])/2,
                                              int(self.canvas['height'])/2,
                                                  text= text,
                                                  fill='white',
                                                  font="Helvetica 20 bold")
        self.canvas.update_idletasks( )


    def _delete_eggtimer(self):
            self.canvas.delete(ALL)
Пример #3
0
class MenuShow:
    """ Displays a menu with optional hint below it. User can traverse the menu and
              select a track using key or button presses.
        Interface:
         * play - displays the menu and selects the first entry
         * input_pressed,  - receives user events passes them to a Player if a track is playing,
                otherwise actions them with _next, _previous, _play_selected_track, _end
         Optional display of eggtimer by means of Players ready_callback
         Supports imageplayer, videoplayer,messagplayer,audioplayer,menushow,mediashow
         Destroys itself on exit
    """

# *********************
# external interface
# ********************

    def __init__(self,
                            show_params,
                             root,
                            canvas,
                            showlist,
                             pp_dir,
                            pp_home,
                            pp_profile):
        """ canvas - the canvas that the menu is to be written on
            show - the name of the configuration dictionary section for the menu
            showlist  - the showlist
            pp_home - Pi presents data_home directory
            pp_profile - Pi presents profile directory"""
        
        self.mon=Monitor()
        self.mon.on()
        
        self.display_guidelines_command=show_params['menu-guidelines']
        self.display_guidelines=self.display_guidelines_command

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

        # open resources
        self.rr=ResourceReader()
        
        # init variables
        self.drawn  = None
        self.player=None
        self.shower=None
        self.menu_timeout_running=None
        self.error=False




    def play(self,show_id,end_callback,ready_callback,top=False,command='nil'):
        """ displays the menu 
              end_callback - function to be called when the menu exits
              ready_callback - callback when menu is ready to display (not used)
              top is True when the show is top level (run from [start])
        """
        
        #instantiate arguments
        self.show_id=show_id
        self.end_callback=end_callback
        self.ready_callback=ready_callback
        self.top=top
        self.command=command

        # check  data files are available.
        self.menu_file = self.pp_profile + "/" + self.show_params['medialist']
        if not os.path.exists(self.menu_file):
            self.mon.err(self,"Medialist file not found: "+ self.menu_file)
            self.end('error',"Medialist file not found")
        
        #create a medialist for the menu and read it.
        self.medialist=MediaList()
        if self.medialist.open_list(self.menu_file,self.showlist.sissue()) == False:
            self.mon.err(self,"Version of medialist different to Pi Presents")
            self.end('error',"Version of medialist different to Pi Presents")

        #get control bindings for this show if top level
        controlsmanager=ControlsManager()
        if self.top==True:
            self.controls_list=controlsmanager.default_controls()
            # and merge in controls from profile
            self.controls_list=controlsmanager.merge_show_controls(self.controls_list,self.show_params['controls'])

           
        if self.show_params['has-background']=="yes":
            background_index=self.medialist.index_of_track ('pp-menu-background')
            if background_index>=0:
                self.menu_img_file = self.complete_path(self.medialist.track(background_index)['location'])
                if not os.path.exists(self.menu_img_file):
                    self.mon.err(self,"Menu background file not found: "+ self.menu_img_file)
                    self.end('error',"Menu background file not found")
            else:
                self.mon.err(self,"Menu background not found in medialist")
                self.end('error',"Menu background not found")
                               
        self.end_menushow_signal= False
        if self.ready_callback<>None:
            self.ready_callback()

        self.menu_timeout_value=int(self.show_params['timeout'])*1000
        self.do_menu()


    def do_menu(self):
        #start timeout alarm if required
        if int(self.show_params['timeout'])<>0:
            self.menu_timeout_running=self.canvas.after(self.menu_timeout_value,self.timeout_menu)

        if self.show_params['menu-background-colour']<>'':
            self.canvas.config(bg=self.show_params['menu-background-colour'])
            
        self.canvas.delete('pp-content')
        self.canvas.update()
        
        # display background image
        if self.show_params['has-background']=="yes":
            self.display_background()

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

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

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

        self.canvas.update_idletasks( )


    #stop received from another concurrent show
    def managed_stop(self):
        if self.menu_timeout_running<>None:
            self.canvas.after_cancel(self.menu_timeout_running)
            self.menu_timeout_running=None
        if self.shower<>None:
            self.shower.managed_stop()
        elif self.player<>None:
            self.end_menushow_signal=True
            self.player.input_pressed('stop')
        else:
            self.end('normal','stopped by ShowManager')
            

    # kill or error received
    def terminate(self,reason):
        if self.menu_timeout_running<>None:
            self.canvas.after_cancel(self.menu_timeout_running)
            self.menu_timeout_running=None
        if self.shower<>None:
            self.shower.terminate(reason)
        elif self.player<>None:
            self.player.terminate(reason)
        else:
            self.end(reason,'Terminated no shower or player running')



   # respond to user inputs.
    def input_pressed(self,symbol,edge,source):
        self.mon.log(self,"Show Id: "+str(self.show_id)+" received key or operation: " + symbol)
        if self.show_params['disable-controls']=='yes':
            return 

        # if at top convert symbolic name to operation otherwise lower down we have received an operation
        # look through list of standard symbols to find match (symbolic-name, function name) operation =lookup (symbol
        if self.top==True:
            operation=self.lookup_control(symbol,self.controls_list)
        else:
            operation=symbol
            
        # print 'operation',operation
        # if no match for symbol against standard operations then return
        if operation=='':
            return
        else:
            if self.shower<>None:
                # if next lower show is running pass down operatin to  the show and lower levels
                self.shower.input_pressed(operation,source,edge) 
            else:
                #service the standard inputs for this show
                if operation=='stop':
                    if self.menu_timeout_running<>None:
                        self.canvas.after_cancel(self.menu_timeout_running)
                        self.menu_timeout_running=None
                    if self.shower<>None:
                        self.shower.input_pressed('stop',edge,source)
                    elif self.player<>None:
                        self.player.input_pressed('stop')
                    else:
                        # not at top so end the show
                        if  self.top == False:
                            self.end('normal',"exit from stop command")
                        else:
                            pass
              
                elif operation in ('up','down'):
                # if child or sub-show running and is a show pass down
                # if  child not running - move
                    if self.shower<>None:
                        self.shower.input_pressed(operation,edge,source)
                    else:
                        if self.player==None:
                            if self.menu_timeout_running<>None:
                                self.canvas.after_cancel(self.menu_timeout_running)
                                self.menu_timeout_running=self.canvas.after(self.menu_timeout_value,self.timeout_menu)
                            if operation=='up':
                                self.previous()
                            else:
                                self.next()
                        
                elif operation =='play':
                    # if child running and is show - pass down
                    # if no track already running  - play
                    if self.shower<>None:
                        self.shower.input_pressed(operation,edge,source)
                    else:
                        if self.player==None:
                            self.play_selected_track(self.medialist.selected_track())

                elif operation == 'pause':
                    # pass down if show or track running.
                    if self.shower<>None:
                        self.shower.input_pressed(operation,edge,source)
                    elif self.player<>None:
                        self.player.input_pressed(operation)
                        
                elif operation[0:4]=='omx-' or operation[0:6]=='mplay-'or operation[0:5]=='uzbl-':
                    if self.player<>None:
                        self.player.input_pressed(operation)


 
    def lookup_control(self,symbol,controls_list):
        for control in controls_list:
            if symbol == control[0]:
                return control[1]
        return ''


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

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

    def timeout_menu(self):
        self.end('normal','menu timeout')
        return
        
    def next(self):     
        self.highlight_menu_entry(self.menu_index,False)
        self.medialist.next('ordered')
        if self.menu_index==self.menu_length-1:
            self.menu_index=0
        else:
            self.menu_index+=1
        self.highlight_menu_entry(self.menu_index,True)     


    def previous(self):   
        self.highlight_menu_entry(self.menu_index,False)
        if self.menu_index==0:
            self.menu_index=self.menu_length-1
        else:
            self.menu_index-=1
        self.medialist.previous('ordered')
        self.highlight_menu_entry(self.menu_index,True)
        

     # at the end of a track just re-display the menu with the original callback from the menu       
    def what_next(self,message):
        # user wants to end
        if self.end_menushow_signal==True:
            self.end_menushow_signal=False
            self.end('normal',"show ended by user")
        else:
            self.do_menu()


# *********************
# Dispatching to Players
# *********************

    def play_selected_track(self,selected_track):
        """ selects the appropriate player from type field of the medialist and computes
              the parameters for that type
              selected track is a dictionary for the track/show
        """
         #remove menu and show working.....        

        if self.menu_timeout_running<>None:
            self.canvas.after_cancel(self.menu_timeout_running)
            self.menu_timeout_running=None
            
        self.canvas.delete('pp-content')
        self.display_eggtimer(self.resource('menushow','m01'))
    
        # dispatch track by type
        self.player=None
        self.shower=None
        track_type = selected_track['type']
        self.mon.log(self,"Track type is: "+ track_type)
        
        if track_type=="video":
            # create a videoplayer
            track_file=self.complete_path(selected_track['location'])
            self.player=VideoPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile)
            self.player.play(track_file,
                                        self.showlist,
                                        self.end_player,
                                        self.delete_eggtimer,
                                        enable_menu=False)
                                        
        elif track_type=="audio":
            # create a audioplayer
            track_file=self.complete_path(selected_track['location'])
            self.player=AudioPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile)
            self.player.play(track_file,
                                        self.showlist,
                                        self.end_player,
                                        self.delete_eggtimer,
                                        enable_menu=False)
                                        
        elif track_type=="image":
            # images played from menus don't have children
            track_file=self.complete_path(selected_track['location'])
            self.player=ImagePlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile)
            self.player.play(track_file,
                                    self.showlist,
                                    self.end_player,
                                    self.delete_eggtimer,
                                    enable_menu=False,
                                    )
            
        elif track_type=="web":
            # create a browser
            track_file=self.complete_path(selected_track['location'])
            self.player=BrowserPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile)
            self.player.play(track_file,
                                        self.showlist,
                                        self.end_player,
                                        self.delete_eggtimer,
                                        enable_menu=False)

                                    
        elif track_type=="message":
            # bit odd because MessagePlayer is used internally to display text. 
            text=selected_track['text']
            self.player=MessagePlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile)
            self.player.play(text,
                                    self.showlist,
                                    self.end_player,
                                    self.delete_eggtimer,
                                    enable_menu=False
                                    )
 
        elif track_type=="show":
            # get the show from the showlist
            index = self.showlist.index_of_show(selected_track['sub-show'])
            if index >=0:
                self.showlist.select(index)
                selected_show=self.showlist.selected_show()
            else:
                self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show'])
                self.end("Unknown show")
          
            if selected_show['type']=="mediashow":    
                self.shower= MediaShow(selected_show,
                                                                self.root,
                                                                self.canvas,
                                                                self.showlist,
                                                               self.pp_dir,
                                                                self.pp_home,
                                                                self.pp_profile)
                self.shower.play(self.show_id,self.end_shower,self.delete_eggtimer,top=False,command='nil')

            elif selected_show['type']=="liveshow":    
                self.shower= LiveShow(selected_show,
                                                                 self.root,
                                                                self.canvas,
                                                                self.showlist,
                                                                self.pp_dir,
                                                                self.pp_home,
                                                                self.pp_profile)
                self.shower.play(self.show_id,self.end_shower,self.delete_eggtimer,top=False,command='nil')

            elif selected_show['type']=="radiobuttonshow":
                self.shower= RadioButtonShow(selected_show,
                                                       self.root,
                                                        self.canvas,
                                                        self.showlist,
                                                         self.pp_dir,
                                                        self.pp_home,
                                                        self.pp_profile)
                self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil')

            elif selected_show['type']=="hyperlinkshow":
                self.shower= HyperlinkShow(selected_show,
                                                        self.root,
                                                        self.canvas,
                                                        self.showlist,
                                                        self.pp_dir,
                                                        self.pp_home,
                                                        self.pp_profile)
                self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil')


            elif selected_show['type']=="menu": 
                self.shower= MenuShow(selected_show,
                                                        self.root,
                                                        self.canvas,
                                                        self.showlist,
                                                        self.pp_dir,
                                                        self.pp_home,
                                                        self.pp_profile)
                self.shower.play(self.show_id,self.end_shower,self.delete_eggtimer,top=False,command='nil')                    
            else:
                self.mon.err(self,"Unknown Show Type: "+ selected_show['type'])
                self.end("Unknown show type")  
                
        else:
            self.mon.err(self,"Unknown Track Type: "+ track_type)
            self.end("Unknown track type")
    
    # callback from when player ends
    def end_player(self,reason,message):
        self.mon.log(self,"Returned from player with message: "+ message)
        self.player=None
        if reason in("killed","error"):
            self.end(reason,message)
        else:
            self.display_eggtimer(self.resource('menushow','m02'))
            self.what_next(message)

    # callback from when shower ends
    def end_shower(self,show_id,reason,message):
        self.mon.log(self,"Returned from shower with message: "+ message)
        self.shower=None
        if reason in ("killed","error"):
            self.end(reason,message)
        else:
            self.display_eggtimer(self.resource('menushow','m03'))
            self.what_next(message)  
   


# *********************
# Ending the show
# *********************
    # finish the player for killing, error or normally
    # this may be called directly if sub/child shows or players are not running
    # if they might be running then need to call terminate?????
    
    def end(self,reason,message):
        self.mon.log(self,"Ending menushow: "+ self.show_params['show-ref'])  
        if self.menu_timeout_running<>None:
            self.canvas.after_cancel(self.menu_timeout_running)
            self.menu_timeout_running=None
        self.end_callback(self.show_id,reason,message)
        self=None
        return


# *********************
# Displaying things
# *********************

    def display_background(self):
        pil_menu_img=PIL.Image.open(self.menu_img_file)
        self.menu_background = PIL.ImageTk.PhotoImage(pil_menu_img)
        self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                      int(self.canvas['height'])/2,
                                      image=self.menu_background,
                                      anchor=CENTER,
                                      tag='pp-content')


   
    
    def display_new_menu(self):

        # calculate menu geometry
        error,reason=self.calculate_geometry()
        if error<>'normal':
            self.mon.err(self,"Menu geometry error: "+ reason)
            self.end('error',"Menu geometry error")
        else:
            # display the menu entries
            self.display_menu_entries()


    def display_menu_entries(self):
        # init the loop
        column_index=0
        row_index=0
        self.menu_length=1
        
        # id store is a list of elements each being a list of the three ids of the elements of the entry
        self.menu_entry_id=[]
        # offsets for the above
        self.icon_id_index=0 # rectangle around the icon
        self.image_id_index=1 # icon image - needed for tkinter
        self.text_id_index=2 # the text - need whn no icon is displayed

        #select the startof the medialist
        self.medialist.start()

        #loop through menu entries
        while True:
            #display the entry
            #calculate top left corner of entry
            self.calculate_entry_position(column_index,row_index)


            # display the button strip
            self.display_entry_strip()

            #display the selected entry highlight
            icon_id=self.display_icon_rectangle()

            #display the image in the icon
            image_id=self.display_icon_image()

            if self.show_params['menu-text-mode']<>'none':
                text_id=self.display_icon_text()
            else:
                text_id=None

            #append id's to the list
            self.menu_entry_id.append([icon_id,image_id,text_id])

            self.canvas.update_idletasks( )            
            #and loop
            if self.medialist.at_end():
                break
            self.menu_length+=1
            self.medialist.next('ordered')

            if self.direction=='horizontal':
                column_index+=1
                if column_index>=self.menu_columns:
                    column_index=0
                    row_index+=1
            else:
                row_index+=1
                if row_index>=self.menu_rows:
                    row_index=0
                    column_index+=1
                    
        # finally select and highlight the first entry
        self.medialist.start()
        self.menu_index=0
        self.highlight_menu_entry(self.menu_index,True)


    def print_geometry(self,total_width,total_height):
        print 'menu width: ', self.menu_width
        print 'columns', self.menu_columns
        print 'icon width: ', self.icon_width
        print 'horizontal padding: ', self.menu_horizontal_padding
        print 'text width: ', self.text_width
        print 'entry width: ', self.entry_width
        print 'total width: ', total_width
        print 'x separation: ', self.x_separation
        print ''
        print 'menu height', self.menu_height
        print 'rows: ', self.menu_rows
        print 'icon height', self.icon_height
        print 'vertical padding: ', self.menu_vertical_padding        
        print 'text height', self.text_height
        print 'entry height', self.entry_height
        print 'total height', total_height
        print 'y separation', self.y_separation

        
    # ------------------------------------------------------------------
    #calculate menu entry size and separation between menu entries
    # ------------------------------------------------------------------
    def calculate_geometry(self):

        self.display_strip=self.show_params['menu-strip']
        self.screen_width=int(self.canvas['width'])
        self.screen_height=int(self.canvas['height'])
        
        if self.display_strip=='yes':
            self.strip_padding=int(self.show_params['menu-strip-padding'])
        else:
            self.strip_padding=0

        # parse the menu window
        error,reason,self.menu_x_left,self.menu_y_top,self.menu_x_right,self.menu_y_bottom=self.parse_menu_window(self.show_params['menu-window'])
        if error<>'normal':
            return 'error',"Menu Window error: "+ reason

        if self.show_params['menu-icon-mode']=='none' and self.show_params['menu-text-mode']=='none':
            return 'error','Icon and Text are both None'
        if self.show_params['menu-icon-mode']=='none' and self.show_params['menu-text-mode']=='overlay':
            return 'error','cannot overlay none icon'

        self.direction=self.show_params['menu-direction']
        
        self.menu_width=self.menu_x_right - self.menu_x_left
        self.menu_height=self.menu_y_bottom - self.menu_y_top

        self.list_length=self.medialist.display_length()

        # get or calculate rows and columns
        if self.direction=='horizontal':
            if self.show_params['menu-columns']=='':
                return 'error','blank columns for horizontal direction'
            self.menu_columns=int(self.show_params['menu-columns'])
            self.menu_rows=self.list_length//self.menu_columns
            if self.list_length % self.menu_columns<>0:
                self.menu_rows+=1
        else:
            if self.show_params['menu-rows']=='':
                return 'error','blank rows for vertical direction'
            self.menu_rows=int(self.show_params['menu-rows'])
            self.menu_columns=self.list_length//self.menu_rows
            if self.list_length % self.menu_rows<>0:
                self.menu_columns+=1
                
        self.x_separation=int(self.show_params['menu-horizontal-separation'])
        self.y_separation=int(self.show_params['menu-vertical-separation'])

        # get size of padding depending on exitence of icon and text
        if self.show_params['menu-icon-mode'] in ('thumbnail','bullet') and self.show_params['menu-text-mode'] == 'right':
            self.menu_horizontal_padding=int(self.show_params['menu-horizontal-padding'])
        else:
            self.menu_horizontal_padding=0

        if self.show_params['menu-icon-mode'] in ('thumbnail','bullet') and self.show_params['menu-text-mode'] == 'below':
            self.menu_vertical_padding=int(self.show_params['menu-vertical-padding'])
        else:
            self.menu_vertical_padding=0
            
        #calculate size of icon depending on use
        if self.show_params['menu-icon-mode'] in ('thumbnail','bullet'):
            self.icon_width=int(self.show_params['menu-icon-width'])
            self.icon_height=int(self.show_params['menu-icon-height'])
        else:
            self.icon_width=0
            self.icon_height=0

        #calculate size of text box depending on mode
        if self.show_params['menu-text-mode']<>'none':
            self.text_width=int(self.show_params['menu-text-width'])
            self.text_height=int(self.show_params['menu-text-height'])
        else:
            self.text_width=0
            self.text_height=0
            
        # calculate size of entry box by combining text and icon sizes
        if self.show_params['menu-text-mode'] == 'right':
            self.entry_width=self.icon_width+self.menu_horizontal_padding+self.text_width
            self.entry_height=max(self.text_height,self.icon_height)
        elif self.show_params['menu-text-mode']=='below':
            self.entry_width=max(self.text_width,self.icon_width)
            self.entry_height=self.icon_height + self.menu_vertical_padding + self.text_height 
        else:
            # no text or overlaid text
            if self.show_params['menu-icon-mode'] in ('thumbnail','bullet'):
                # icon only
                self.entry_width=self.icon_width
                self.entry_height=self.icon_height
            else:
                #text only
                self.entry_width=self.text_width
                self.entry_height=self.text_height

        if self.entry_width<=self.menu_horizontal_padding:
            return 'error','entry width is zero'

        if self.entry_height<=self.menu_vertical_padding:
            return 'error','entry height is zero'

        # calculate totals for debugging puropses
        total_width=self.menu_columns * self.entry_width +(self.menu_columns-1)*self.x_separation
        total_height=self.menu_rows * self.entry_height + (self.menu_rows-1)*self.y_separation
        
        # self.print_geometry(total_width,total_height)   


        # display guidelines and debgging text if there is a problem     
        if total_width>self.menu_width and self.display_guidelines<>'never':
                self.display_guidelines='always'
                self.mon.log(self,'\nMENU IS WIDER THAN THE WINDOW')
                self.print_geometry(total_width,total_height)


        if total_height>self.menu_height and self.display_guidelines<>'never':
                self.display_guidelines='always'
                self.mon.log(self,'\nMENU IS TALLER THAN THE WINDOW')
                self.print_geometry(total_width,total_height)            

        # display calculated total rectangle guidelines for debugging
        if self.display_guidelines=='always':
            points=[self.menu_x_left,self.menu_y_top, self.menu_x_left+total_width,self.menu_y_top+total_height]

            # and display the icon rectangle
            self.canvas.create_rectangle(points,
                                           outline='red',
                                           fill='',
                                           tag='pp-content')

        
        # display menu rectangle guidelines for debugging
        if self.display_guidelines=='always':
            points=[self.menu_x_left,self.menu_y_top, self.menu_x_right,self.menu_y_bottom]
            self.canvas.create_rectangle(points,
                                           outline='blue',
                                           fill='',
                                           tag='pp-content')
                
        return 'normal',''

    def calculate_entry_position(self,column_index,row_index):
            self.entry_x=self.menu_x_left+ column_index*(self.x_separation+self.entry_width)
            self.entry_y=self.menu_y_top+ row_index*(self.y_separation+self.entry_height)

            
    def display_entry_strip(self):
        if self.display_strip=='yes':
            if self.direction=='vertical':
                    #display the strip
                    strip_points=[self.entry_x - self.strip_padding -1 ,
                                  self.entry_y - self.strip_padding - 1,
                                  self.entry_x+ self.entry_width + self.strip_padding - 1,
                                  self.entry_y+self.entry_height+ self.strip_padding - 1]
                    self.canvas.create_rectangle(strip_points,
                                                       outline='',
                                                        fill='gray',
                                                       stipple='gray12',                                 
                                                       tag='pp-content')

                    top_l_points=[self.entry_x - self.strip_padding,
                                  self.entry_y - self.strip_padding,
                                  self.entry_x + self.entry_width + self.strip_padding ,
                                  self.entry_y - self.strip_padding]
                    
                    self.canvas.create_line(top_l_points,
                                            fill='light gray',
                                            tag='pp-content')
                    
                    bottom_l_points=[self.entry_x - self.strip_padding,
                                     self.entry_y + self.entry_height + self.strip_padding,
                                     self.entry_x+ self.entry_width + self.strip_padding ,
                                     self.entry_y+ self.entry_height + self.strip_padding]
                    
                    self.canvas.create_line(bottom_l_points,
                                            fill='dark gray',
                                            tag='pp-content')

                    left_l_points=[self.entry_x - self.strip_padding,
                                   self.entry_y - self.strip_padding,
                                   self.entry_x - self.strip_padding,
                                   self.entry_y + self.entry_height + self.strip_padding]
                    
                    self.canvas.create_line(left_l_points,
                                            fill='gray',
                                            tag='pp-content')

            else:
                    #display the strip vertically
                    strip_points=[self.entry_x - self.strip_padding +1 ,
                                  self.entry_y - self.strip_padding +1,
                                  self.entry_x+self.entry_width + self.strip_padding -1,
                                  self.entry_y + self.entry_height+ self.strip_padding -1]
                    
                    self.canvas.create_rectangle(strip_points,
                                                       outline='',
                                                        fill='gray',
                                                       stipple='gray12',                                 
                                                       tag='pp-content')

                    top_l_points=[self.entry_x - self.strip_padding,
                                  self.entry_y - self.strip_padding,
                                  self.entry_x + self.entry_width + self.strip_padding,
                                  self.entry_y - self.strip_padding]
                    
                    self.canvas.create_line(top_l_points,
                                            fill='light gray',
                                            tag='pp-content')
                    
                    left_l_points=[self.entry_x - self.strip_padding,
                                   self.entry_y - self.strip_padding,
                                   self.entry_x - self.strip_padding,
                                   self.entry_y + self.entry_height+ self.strip_padding]
                    
                    self.canvas.create_line(left_l_points,
                                            fill='gray',
                                            tag='pp-content')

                    right_l_points=[self.entry_x +self.entry_width + self.strip_padding,
                                     self.entry_y - self.strip_padding,
                                     self.entry_x +self.entry_width + self.strip_padding,
                                     self.entry_y + self.entry_height+ self.strip_padding]
                    
                    self.canvas.create_line(right_l_points,
                                            fill='dark gray',
                                            tag='pp-content')


    # display the rectangle that goes arond the icon when the entry is selected
    def display_icon_rectangle(self):
            if self.show_params['menu-icon-mode'] in ('thumbnail','bullet'):

                #calculate icon parameters
                if self.icon_width<self.text_width and self.show_params['menu-text-mode']=='below':
                        self.icon_x_left=self.entry_x+abs(self.icon_width-self.text_width)/2
                else:
                        self.icon_x_left=self.entry_x
                self.icon_x_right=self.icon_x_left+self.icon_width

                if self.icon_height<self.text_height and self.show_params['menu-text-mode']=='right':
                        self.icon_y_top=self.entry_y+abs(self.icon_height-self.text_height)/2
                else:
                        self.icon_y_top=self.entry_y
                self.icon_y_bottom=self.icon_y_top+self.icon_height

                
                req_horiz_sep=self.menu_horizontal_padding
                req_vert_sep=self.menu_vertical_padding

                
                points=[self.icon_x_left,self.icon_y_top,self.icon_x_right,self.icon_y_top,self.icon_x_right,self.icon_y_bottom,self.icon_x_left,self.icon_y_bottom]

                # display guidelines make it white when not selctedfor debugging
                if self.display_guidelines=='always':
                    outline='white'
                else:
                    outline=''

                # and display the icon rectangle
                icon_id=self.canvas.create_polygon(points,
                                                   outline=outline,
                                                   fill='',
                                                   tag='pp-content')


            else:
                # not using icon so set starting point for text to zero icon size
                self.icon_x_right=self.entry_x
                self.icon_y_bottom=self.entry_y
                req_horiz_sep=0
                req_vert_sep=0
                icon_id=None
            return icon_id
        

    #display the image in a menu entry
    def  display_icon_image(self):
            image_id=None
            if self.show_params['menu-icon-mode'] == 'thumbnail':
                # try for the thumbnail
                if self.medialist.selected_track()['thumbnail']<>'' and os.path.exists(self.complete_path(self.medialist.selected_track()['thumbnail'])):
                    self.pil_image=PIL.Image.open(self.complete_path(self.medialist.selected_track()['thumbnail']))
                else:
                    #cannot find thumbnail get the image if its an image track
                    if self.medialist.selected_track()['type'] =='image':
                        self.track=self.complete_path(self.medialist.selected_track()['location'])
                    else:
                        self.track=''
                    if self.medialist.selected_track()['type']=='image' and os.path.exists(self.track)==True: 
                        self.pil_image=PIL.Image.open(self.track)
                    else:
                        #use a standard thumbnail
                        type=self.medialist.selected_track()['type']
                        standard=self.pp_dir+os.sep+'pp_home'+os.sep+'pp_resources'+os.sep+type+'.png'
                        if os.path.exists(standard)==True:
                            self.pil_image=PIL.Image.open(standard)
                            self.mon.log(self,'WARNING: default thumbnail used for '+self.medialist.selected_track()['title'])
                        else:
                            self.pil_image=None

                # display the image                
                if self.pil_image<>None:
                    self.pil_image=self.pil_image.resize((self.icon_width-2,self.icon_height-2))                 
                    image_id=PIL.ImageTk.PhotoImage(self.pil_image)
                    self.canvas.create_image(self.icon_x_left+1, self.icon_y_top+1,
                                                image=image_id, anchor=NW,
                                                 tag='pp-content')
                else:
                        image_id=None
                        
            elif self.show_params['menu-icon-mode'] =='bullet':
                    bullet=self.complete_path(self.show_params['menu-bullet'])                  
                    if os.path.exists(bullet)==False:
                        self.pil_image=None                          
                    else:
                        self.pil_image=PIL.Image.open(bullet)
                    if self.pil_image<>None:
                        self.pil_image=self.pil_image.resize((self.icon_width-2,self.icon_height-2))                 
                        image_id=PIL.ImageTk.PhotoImage(self.pil_image)
                        self.canvas.create_image(self.icon_x_left+1, self.icon_y_top+1,
                                                      image=image_id, anchor=NW,
                                                      tag='pp-content')                                      
            else:
                image_id=None
            return image_id

            
    #display the text of a menu entry
    def display_icon_text(self):
            text_mode=self.show_params['menu-text-mode']
            if self.show_params['menu-icon-mode'] in ('thumbnail','bullet'):
                if text_mode=='right':
                    if self.icon_height>self.text_height:
                        text_y_top=self.entry_y+abs(self.icon_height-self.text_height)/2
                    else:
                        text_y_top=self.entry_y
                    text_y_bottom=text_y_top+self.text_height
                    
                    text_x_left=self.icon_x_right+self.menu_horizontal_padding
                    text_x_right=text_x_left+self.text_width
                    
                    text_x=text_x_left
                    text_y=text_y_top+(self.text_height/2)

                elif text_mode=='below':
                    text_y_top=self.icon_y_bottom+self.menu_vertical_padding
                    text_y_bottom=text_y_top+self.text_height
                    
                    if self.icon_width>self.text_width:
                        text_x_left=self.entry_x+abs(self.icon_width-self.text_width)/2
                    else:
                        text_x_left=self.entry_x
                    text_x_right=text_x_left+self.text_width
                    
                    text_x=text_x_left+(self.text_width/2)
                    text_y=text_y_top

                else:
                    # icon with text_mode=overlay or none
                    text_x_left=self.icon_x_left
                    text_x_right= self.icon_x_right
                    text_y_top=self.icon_y_top
                    text_y_bottom=self.icon_y_bottom
                    text_x=(text_x_left+text_x_right)/2
                    text_y=(text_y_top+text_y_bottom)/2                    

            else:
                    #no icon text only
                    text_y_top=self.entry_y
                    text_y_bottom=text_y_top+self.text_height
                    text_x_left=self.entry_x
                    text_x_right=text_x_left+self.text_width
                    text_x=self.entry_x
                    text_y=self.entry_y+self.text_height/2


            #display the guidelines for debugging
            if self.display_guidelines=='always':
                points=[text_x_left,text_y_top,text_x_right,text_y_top,text_x_right,text_y_bottom,text_x_left,text_y_bottom]
                self.canvas.create_polygon(points,fill= '' ,
                                              outline='white',
                                              tag='pp-content')

            # display the text
            if text_mode=='below' and self.show_params['menu-icon-mode']  in ('thumbnail','bullet'):
                anchor=N
                justify=CENTER
            elif text_mode=='overlay' and self.show_params['menu-icon-mode']  in ('thumbnail','bullet'):
                anchor=CENTER
                justify=CENTER
            else:
                anchor=W
                justify=LEFT
            text_id=self.canvas.create_text(text_x,text_y,
                                       text=self.medialist.selected_track()['title'],
                                       anchor=anchor,
                                       fill=self.show_params['entry-colour'],
                                       font=self.show_params['entry-font'],
                                       width=self.text_width,
                                       justify=justify,
                                       tag='pp-content')
            return text_id
        

    def highlight_menu_entry(self,index,state):
        if self.show_params['menu-icon-mode']<>'none':
            if state==True:
                self.canvas.itemconfig(self.menu_entry_id[index][self.icon_id_index],
                                       outline=self.show_params['entry-select-colour'],
                                       width=4,
                                       )
            else:
                self.canvas.itemconfig(self.menu_entry_id[index][self.icon_id_index],
                                        outline='',
                                       width=1
                                       )
        else:
            if state==True:
                self.canvas.itemconfig(self.menu_entry_id[index][self.text_id_index],
                                       fill=self.show_params['entry-select-colour'])
            else:
                self.canvas.itemconfig(self.menu_entry_id[index][self.text_id_index],
                                    fill=self.show_params['entry-colour'])
                

    
    def display_eggtimer(self,text):
        # print "display eggtimer"
        self.canvas.create_text(int(self.canvas['width'])/2,
                                              int(self.canvas['height'])/2,
                                                  text= text,
                                                  fill='white',
                                                  font="Helvetica 20 bold",
                                                   tag='pp-eggtimer')
        self.canvas.update_idletasks( )


    def delete_eggtimer(self):
        # print"delete eggtimer"
        self.canvas.delete('pp-eggtimer')
        self.canvas.update_idletasks( )


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

    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file<>'' and track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        return track_file     

    def parse_menu_window(self,line):
            if line<>'':
                fields = line.split()
                if len(fields) not in  (1, 2,4):
                    return 'error','wrong number of fields',0,0,0,0
                if len(fields)==1:
                    if fields[0]=='fullscreen':
                        return 'normal','',0,0,self.screen_width - 1, self.screen_height - 1
                    else:
                        return 'error','single field is not fullscreen',0,0,0,0
                if len(fields)==2:                    
                    if fields[0].isdigit() and fields[1].isdigit():
                        return 'normal','',int(fields[0]),int(fields[1]),self.screen_width, self.screen_height
                    else:
                        return 'error','field is not a digit',0,0,0,0
                if len(fields)==4:                    
                    if fields[0].isdigit() and fields[1].isdigit() and fields[2].isdigit() and fields[3].isdigit():
                        return 'normal','',int(fields[0]),int(fields[1]),int(fields[2]),int(fields[3])
                else:
                     return 'error','field is not a digit',0,0,0,0
            else:
                     return 'error','line is blank',0,0,0,0


    def resource(self,section,item):
        value=self.rr.get(section,item)
        if value==False:
            self.mon.err(self, "resource: "+section +': '+ item + " not found" )
            # timers may be running so need terminate
            self.terminate("error")
        else:
            return value
Пример #4
0
class MediaShow:

    # *******************
    # External interface
    # ********************

    def __init__(self, show, canvas, showlist, pp_home, pp_profile):
        """ canvas - the canvas that the menu is to be written on
            show - the dictionary fo the show to be played
            pp_home - Pi presents data_home directory
            pp_profile - Pi presents profile directory
        """

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

        #instantiate arguments
        self.show = show
        self.showlist = showlist
        self.canvas = canvas
        self.pp_home = pp_home
        self.pp_profile = pp_profile

        # open resources
        self.rr = ResourceReader()

        # Init variables
        self.player = None
        self.shower = None
        self._poll_for_interval_timer = None
        self._poll_for_continue_timer = None
        self._waiting_for_interval = False
        self._interval_timer = None
        self.error = False

        self._interval_timer_signal = False
        self._end_mediashow_signal = False
        self._next_track_signal = False
        self._previous_track_signal = False
        self._play_child_signal = False
        self._req_next = 'nil'

        self._state = 'closed'

    def play(self,
             end_callback,
             ready_callback=None,
             top=False,
             command='nil'):
        """ displays the mediashow
              end_callback - function to be called when the menu exits
              ready_callback - callback when menu is ready to display (not used)
              top is True when the show is top level (run from [start])
        """

        #instantiate the arguments
        self._end_callback = end_callback
        self._ready_callback = ready_callback
        self.top = top
        self.command = command
        self.mon.log(self, "Starting show: " + self.show['show-ref'])

        # check  data files are available.
        self.media_file = self.pp_profile + "/" + self.show['medialist']
        if not os.path.exists(self.media_file):
            self.mon.err(self, "Medialist file not found: " + self.media_file)
            self._end('error', "Medialist file not found")

        #create a medialist for the mediashow and read it.
        self.medialist = MediaList()
        if self.medialist.open_list(self.media_file,
                                    self.showlist.sissue()) == False:
            self.mon.err(self, "Version of medialist different to Pi Presents")
            self._end('error', "Version of medialist different to Pi Presents")

        self._wait_for_trigger()

# respond to key presses.

    def key_pressed(self, key_name):
        self.mon.log(self, "received key: " + key_name)

        if key_name == '':
            pass

        elif key_name == 'escape':
            # if next lower show is running pass down to stop the show and lower level
            if self.shower <> None:
                self.shower.key_pressed(key_name)
            # if not at top stop the show
            else:
                if self.top == False:
                    self._end_mediashow_signal = True
                    # and if a track is running stop that first
                    if self.player <> None:
                        self.player.key_pressed(key_name)
                else:
                    # at top level in a manual presentation stop the track
                    if self.show['progress'] == 'manual':
                        if self.player <> None:
                            self.player.key_pressed(key_name)

        elif key_name in ('up', 'down'):
            # if child or sub-show is running and is a show pass to show, track does not use up/down
            # otherwise use keys for next or previous
            if self.shower <> None:
                self.shower.key_pressed(key_name)
            else:
                if key_name == 'up':
                    self._previous()
                else:
                    self._next()

        elif key_name == 'return':
            # if child show or sub-show is running and is show - pass down- player does not use return
            # ELSE use Return to start child or to start the show if waiting
            if self.shower <> None:
                self.shower.key_pressed(key_name)
            else:
                if self._state == 'playing':
                    if self.show['has-child'] == 'yes':
                        self._play_child_signal = True
                        # and stop the current track if its running
                        if self.player <> None:
                            self.player.key_pressed("escape")
                else:
                    self._start_show()

        elif key_name == 'pir':
            self._start_show()

        elif key_name in ('p', ' '):
            # pass down if show or track running.
            if self.shower <> None:
                self.shower.key_pressed(key_name)
            elif self.player <> None:
                self.player.key_pressed(key_name)

    def button_pressed(self, button, edge):
        if button == 'play': self.key_pressed("return")
        elif button == 'up': self.key_pressed("up")
        elif button == 'down': self.key_pressed("down")
        elif button == 'stop': self.key_pressed("escape")
        elif button == 'pause': self.key_pressed('p')
        elif button == 'PIR': self.key_pressed('pir')

    # kill or error
    def terminate(self, reason):
        if self.shower <> None:
            self.mon.log(self, "sent terminate to shower")
            self.shower.terminate(reason)
        elif self.player <> None:
            self.mon.log(self, "sent terminate to player")
            self.player.terminate(reason)
        else:
            self._end(reason,
                      'terminated without terminating shower or player')

    def _tidy_up(self):
        if self._poll_for_continue_timer <> None:
            self.canvas.after_cancel(self._poll_for_continue_timer)
            self._poll_for_continue_timer = None
        if self._poll_for_interval_timer <> None:
            self.canvas.after_cancel(self._poll_for_interval_timer)
            self._poll_for_interval_timer = None
        if self._interval_timer <> None:
            self.canvas.after_cancel(self._interval_timer)
            self._interval_timer = None

    def resource(self, section, item):
        value = self.rr.get(section, item)
        if value == False:
            self.mon.err(self,
                         "resource: " + section + ': ' + item + " not found")
            self.terminate("error")
        else:
            return value

# ***************************
# Respond to key/button presses
# ***************************

    def _stop(self, message):
        self._end_mediashow_signal = True
        if self._interval_timer <> None:
            self.canvas.after_cancel(self._interval_timer)

    def _next(self):
        # stop track if running and set signal
        self._next_track_signal = True
        if self.shower <> None:
            self.shower.key_pressed("escape")
        else:
            if self.player <> None:
                self.player.key_pressed("escape")

    def _previous(self):
        self._previous_track_signal = True
        if self.shower <> None:
            self.shower.key_pressed("escape")
        else:
            if self.player <> None:
                self.player.key_pressed("escape")

# ***************************
# end of show functions
# ***************************

    def _end(self, reason, message):
        self._end_mediashow_signal = False
        self.mon.log(self, "Ending Mediashow: " + self.show['show-ref'])
        self._tidy_up()
        self._end_callback(reason, message)
        self = None
        return

# ***************************
# Show sequencer
# ***************************

    def _wait_for_trigger(self):
        self._state = 'waiting'
        if self.ready_callback <> None:
            self.ready_callback()

        self.mon.log(self, "Waiting for trigger: " + self.show['trigger'])

        if self.show['trigger'] == "button":
            # blank screen waiting for trigger if auto, otherwise display something
            if self.show['progress'] == "manual":
                text = self.resource('mediashow', 'm01')
            else:
                text = ""
            self.display_message(self.canvas, 'text', text, 0,
                                 self._start_show)

        elif self.show['trigger'] == "PIR":
            # blank screen waiting for trigger
            text = self.resource('mediashow', 'm02')
            self.display_message(self.canvas, 'text', text, 0,
                                 self._start_show)

        elif self.show['trigger'] == "start":
            self._start_show()

        else:
            self.mon.err(self, "Unknown trigger: " + self.show['trigger'])
            self._end('error', "Unknown trigger type")

    def _start_show(self):
        self._state = 'playing'
        self._direction = 'forward'
        # start interval timer
        if self.show[
                'repeat'] == "interval" and self.show['repeat-interval'] <> 0:
            self._interval_timer_signal = False
            self._interval_timer = self.canvas.after(
                int(self.show['repeat-interval']) * 1000,
                self._end_interval_timer)
        # and play the first track unless commanded otherwise
        if self.command == 'backward':
            self.medialist.finish()
        else:
            self.medialist.start()
        self._play_selected_track(self.medialist.selected_track())

    def _what_next(self):
        self._direction = 'forward'

        # user wants to end, wait for any shows or tracks to have ended then end show
        if self._end_mediashow_signal == True:
            if self.player == None and self.shower == None:
                self._end_mediashow_signal = False
                self._end('normal', "show ended by user")
            else:
                pass

        #returning from a subshow needing to move onward
        elif self._req_next == 'do-next':
            self._req_next = 'nil'
            self.medialist.next()
            self._play_selected_track(self.medialist.selected_track())

        #returning from a subshow needing to move backward
        elif self._req_next == 'do-previous':
            self._req_next = 'nil'
            self._direction = 'backward'
            self.medialist.previous()
            self._play_selected_track(self.medialist.selected_track())

        # user wants to play child
        elif self._play_child_signal == True:
            self._play_child_signal = False
            index = self.medialist.index_of_track('pp-child-show')
            if index >= 0:
                #don't select the track as need to preserve mediashow sequence.
                child_track = self.medialist.track(index)
                self._display_eggtimer(self.resource('mediashow', 'm07'))
                self._play_selected_track(child_track)
            else:
                self.mon.err(
                    self, "Child show not found in medialist: " +
                    self.show['pp-child-show'])
                self._end('error', "child show not found in medialist")

        # skip to next track on user input
        elif self._next_track_signal == True:
            self._next_track_signal = False
            if self.medialist.at_end() == True:
                if self.show['sequence'] == "ordered" and self.show[
                        'repeat'] == 'oneshot' and self.top == False:
                    self._end('do-next', "Return from Sub Show")
                else:
                    self.medialist.next()
                    self._play_selected_track(self.medialist.selected_track())
            else:
                self.medialist.next()
                self._play_selected_track(self.medialist.selected_track())

        # skip to previous track on user input
        elif self._previous_track_signal == True:
            self._previous_track_signal = False
            self._direction = 'backward'
            if self.medialist.at_start() == True:
                if self.show['sequence'] == "ordered" and self.show[
                        'repeat'] == 'oneshot' and self.top == False:
                    self._end('do-previous', "Return from Sub Show")
                else:
                    self.medialist.previous()
                    self._play_selected_track(self.medialist.selected_track())
            else:
                self.medialist.previous()
                self._play_selected_track(self.medialist.selected_track())

        # track is finished and we are on auto
        elif self.show['progress'] == "auto":
            if self.medialist.at_end() == True:
                if self.show['sequence'] == "ordered" and self.show[
                        'repeat'] == 'oneshot' and self.top == False:
                    self._end('do-next', "Return from Sub Show")

                #### elif
                elif self.show['sequence'] == "ordered" and self.show[
                        'repeat'] == 'oneshot' and self.top == True:
                    self._wait_for_trigger()

                elif self._waiting_for_interval == True:
                    if self._interval_timer_signal == True:
                        self._interval_timer_signal = False
                        self._waiting_for_interval = False
                        self._start_show()
                    else:
                        self._poll_for_interval_timer = self.canvas.after(
                            1000, self._what_next)

                elif self.show['sequence'] == "ordered" and self.show[
                        'repeat'] == 'interval' and int(
                            self.show['repeat-interval']) > 0:
                    self._waiting_for_interval = True
                    self._poll_for_interval_timer = self.canvas.after(
                        1000, self._what_next)

                elif self.show['sequence'] == "ordered" and self.show[
                        'repeat'] == 'interval' and int(
                            self.show['repeat-interval']) == 0:
                    self.medialist.next()
                    self._play_selected_track(self.medialist.selected_track())

                else:
                    self.mon.err(self, "Unhandled playing event: ")
                    self._end('error', "Unhandled playing event")

            else:
                self.medialist.next()
                self._play_selected_track(self.medialist.selected_track())

        # track has finished and we are on manual progress
        elif self.show['progress'] == "manual":
            self._delete_eggtimer()
            self._display_eggtimer(self.resource('mediashow', 'm03'))
            self._poll_for_continue_timer = self.canvas.after(
                500, self._what_next)

        else:
            #unhandled state
            self.mon.err(self, "Unhandled playing event: ")
            self._end('error', "Unhandled playing event")

    def _end_interval_timer(self):
        self._interval_timer_signal = True


# ***************************
# Dispatching to Players/Shows
# ***************************

# used to display internal messages in situations where a medialist entry could be used.

    def display_message(self, canvas, source, content, duration,
                        _display_message_callback):
        self._display_message_callback = _display_message_callback
        tp = {
            'duration': duration,
            'message-colour': 'white',
            'message-font': 'Helvetica 20 bold'
        }
        self.player = MessagePlayer(canvas, tp, tp)
        self.player.play(content, self._display_message_end, None)

    def _display_message_end(self, reason, message):
        self.player = None
        if reason in ('error', 'killed'):
            self._end(reason, message)
        else:
            self._display_message_callback()

    def complete_path(self, selected_track):
        #  complete path of the filename of the selected entry
        track_file = selected_track['location']
        if track_file[0] == "+":
            track_file = self.pp_home + track_file[1:]
        self.mon.log(self, "Track to play is: " + track_file)
        return track_file

    def _play_selected_track(self, selected_track):
        """ selects the appropriate player from type field of the medialist and computes
              the parameters for that type
              selected track is a dictionary for the track/show
        """
        self.canvas.delete(ALL)
        if self.show['progress'] == "manual":
            self._display_eggtimer(self.resource('mediashow', 'm04'))

        # is menu required
        if self.show['has-child'] == "yes":
            enable_child = True
        else:
            enable_child = False

        #dispatch track by type
        self.player = None
        self.shower = None
        track_type = selected_track['type']
        self.mon.log(self, "Track type is: " + track_type)

        if track_type == "video":
            # create a videoplayer
            track_file = self.complete_path(selected_track)
            self.player = VideoPlayer(self.canvas, self.show, selected_track)
            self.player.play(track_file,
                             self.end_player,
                             self.ready_callback,
                             enable_menu=enable_child)

        elif track_type == "image":
            track_file = self.complete_path(selected_track)
            # images played from menus don't have children
            self.player = ImagePlayer(self.canvas, self.show, selected_track)
            self.player.play(track_file,
                             self.end_player,
                             self.ready_callback,
                             enable_menu=enable_child)

        elif track_type == "message":
            # bit odd because MessagePlayer is used internally to display text.
            text = selected_track['text']
            self.player = MessagePlayer(self.canvas, self.show, selected_track)
            self.player.play(text,
                             self.end_player,
                             self.ready_callback,
                             enable_menu=enable_child)

        elif track_type == "show":
            # get the show from the showlist
            index = self.showlist.index_of_show(selected_track['sub-show'])
            if index >= 0:
                self.showlist.select(index)
                selected_show = self.showlist.selected_show()
            else:
                self.mon.err(
                    self, "Show not found in showlist: " +
                    selected_track['sub-show'])
                self._end('error', "Unknown show")

            if selected_show['type'] == "mediashow":
                self.shower = MediaShow(selected_show, self.canvas,
                                        self.showlist, self.pp_home,
                                        self.pp_profile)
                self.shower.play(self.end_shower,
                                 top=False,
                                 command=self._direction)

            elif selected_show['type'] == "liveshow":
                self.shower = LiveShow(selected_show, self.canvas,
                                       self.showlist, self.pp_home,
                                       self.pp_profile)
                self.shower.play(self.end_shower, top=False, command='nil')

            elif selected_show['type'] == "menu":
                self.shower = MenuShow(selected_show, self.canvas,
                                       self.showlist, self.pp_home,
                                       self.pp_profile)
                self.shower.play(self.end_shower, top=False, command='nil')

            else:
                self.mon.err(self,
                             "Unknown Show Type: " + selected_show['type'])
                self._end('error' "Unknown show type")

        else:
            self.mon.err(self, "Unknown Track Type: " + track_type)
            self._end('error', "Unknown track type")

    def ready_callback(self):
        self._delete_eggtimer()

    def end_player(self, reason, message):
        self._req_next = 'nil'
        self.mon.log(self, "Returned from player with message: " + message)
        self.player = None
        if reason in ("killed", "error"):
            self._end(reason, message)
        elif self.show['progress'] == "manual":
            self._display_eggtimer(self.resource('mediashow', 'm05'))
            self._req_next = reason
            self._what_next()
        else:
            self._req_next = reason
            self._what_next()

    def end_shower(self, reason, message):
        self._req_next = 'nil'
        self.mon.log(self, "Returned from shower with message: " + message)
        self.shower = None
        if reason in ("killed", "error"):
            self._end(reason, message)
        elif self.show['progress'] == "manual":
            self._display_eggtimer(self.resource('mediashow', 'm06'))
            self._req_next = reason
            self._what_next()
        else:
            self._req_next = reason
            self._what_next()

    def _display_eggtimer(self, text):
        self.canvas.create_text(int(self.canvas['width']) / 2,
                                int(self.canvas['height']) / 2,
                                text=text,
                                fill='white',
                                font="Helvetica 20 bold")
        self.canvas.update_idletasks()

    def _delete_eggtimer(self):
        self.canvas.delete(ALL)
Пример #5
0
class MediaShow:


# *******************
# External interface
# ********************

    def __init__(self,
                            show_params,
                             root,
                            canvas,
                            showlist,
                            pp_dir,
                            pp_home,
                            pp_profile):
        """ canvas - the canvas that the menu is to be written on
            show - the dictionary fo the show to be played
            pp_home - Pi presents data_home directory
            pp_profile - Pi presents profile directory
        """

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

        # open resources
        self.rr=ResourceReader()

        # Init variables
        self.player=None
        self.shower=None
        self.poll_for_interval_timer=None
        self.poll_for_continue_timer=None
        self.waiting_for_interval=False
        self.interval_timer=None
        self.duration_timer=None
        self.error=False
        
        self.interval_timer_signal=False
        self.end_trigger_signal=False
        self.end_mediashow_signal=False
        self.next_track_signal=False
        self.previous_track_signal=False
        self.play_child_signal = False
        self.req_next='nil'

        #create and instance of TimeOfDay scheduler so we can add events
        self.tod=TimeOfDay()

        self.state='closed'


    def play(self,show_id,end_callback,show_ready_callback, top=False,command='nil'):

        """ displays the mediashow
              end_callback - function to be called when the menu exits
              ready_callback - callback when menu is ready to display (not used)
              top is True when the show is top level (run from [start])
        """

        #instantiate the arguments
        self.show_id=show_id
        self.end_callback=end_callback
        self.show_ready_callback=show_ready_callback
        self.top=top
        self.command=command
        self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Starting show")

        # check  data files are available.
        self.media_file = self.pp_profile + "/" + self.show_params['medialist']
        if not os.path.exists(self.media_file):
            self.mon.err(self,"Medialist file not found: "+ self.media_file)
            self.end('error',"Medialist file not found")


        #create a medialist for the mediashow and read it.
        self.medialist=MediaList(self.show_params['sequence'])
        if self.medialist.open_list(self.media_file,self.showlist.sissue())==False:
            self.mon.err(self,"Version of medialist different to Pi Presents")
            self.end('error',"Version of medialist different to Pi Presents")

        #get controls for this show if top level
        controlsmanager=ControlsManager()
        if self.top==True:
            self.controls_list=controlsmanager.default_controls()
            # and merge in controls from profile
            self.controls_list=controlsmanager.merge_show_controls(self.controls_list,self.show_params['controls'])


        #set up the time of day triggers for the show
        if self.show_params['trigger']in('time','time-quiet'):
            error_text=self.tod.add_times(self.show_params['trigger-input'],id(self),self.tod_start_callback,self.show_params['trigger'])
            if error_text<>'':
                self.mon.err(self,error_text)
                self.end('error',error_text)

        if self.show_params['trigger-end']=='time':
            # print self.show_params['trigger-end-time']
            error_text=self.tod.add_times(self.show_params['trigger-end-time'],id(self),self.tod_end_callback,'n/a')
            if error_text<>'':
                self.mon.err(self,error_text)
                self.end('error',error_text)
                
        if self.show_params['trigger-end']=='duration':
            error_text=self.calculate_duration(self.show_params['trigger-end-time'])
            if error_text<>'':
                self.mon.err(self,error_text)
                self.end('error',error_text)
                
        self.state='closed'
        self.egg_timer=None
        self.wait_for_trigger()


# ********************************
# Respond to external events
# ********************************

    #stop received from another concurrent show
    def managed_stop(self):
           # if next lower show is running pass down to stop the show and lower level
            if self.shower<>None:
                self.shower.managed_stop()
            else:
                #stop the show if not at top
                self.end_mediashow_signal=True
                # and if track is runing stop that first
                if self.player<>None:
                    self.player.input_pressed('stop')

    # kill or error
    def terminate(self,reason):
        if self.shower<>None:
            self.shower.terminate(reason)
        elif self.player<>None:
            self.player.terminate(reason)
        else:
            self.end(reason,' terminated with no shower or player to terminate')


   
   # respond to input events
    def input_pressed(self,symbol,edge,source):
        self.mon.log(self, self.show_params['show-ref']+ ' '+ str(self.show_id)+": received input: " + symbol)

        
        #  check symbol against mediashow triggers, triggers can be used at top or lower level
        # and not affected by disable-controls

        if self.state=='waiting' and self.show_params['trigger'] in ('input','input-quiet')and symbol == self.show_params['trigger-input']:
            self.start_show()
        elif self.state=='playing' and self.show_params['trigger-next']=='input' and symbol == self.show_params['next-input']:
            self.next()

       # internal functions are triggered only when disable-controls is  'no'
        if self.show_params['disable-controls']=='yes':
            return

        # if at top convert symbolic name to operation otherwise lower down we have received an operation
        # look through list of standard symbols to find match (symbolic-name, function name) operation =lookup (symbol
        if self.top==True:
            operation=self.lookup_control(symbol,self.controls_list)
        else:
            operation=symbol
            
   
        # print 'operation',operation
        self.do_operation(operation,edge,source)


    #service the standard inputs for this show
    def do_operation(self,operation,edge,source):
        if self.shower<>None:
            # if next lower show is running pass down to stop the show and lower level
            self.shower.input_pressed(operation,edge,source) 
        else:        
            # control this show and its tracks
            # print 'operation',operation
            if operation=='stop':
                if self.top == False:
                    # not at top so stop the current show 
                    self.end_mediashow_signal=True
                    # and if a track is running stop that first
                    if self.player<>None:
                        self.player.input_pressed('stop')
                else:
                    # top = True, just stop track if running
                    if self.player<>None:
                        self.player.input_pressed('stop')

            elif operation in ('up','down'):
                #if playing rather than waiting use keys for next or previous
                if operation=='up' and self.state=='playing':
                    self.previous()
                else:
                    self.next()

            elif operation=='play':
                # use 'play' to start child if state=playing or to trigger the show if waiting for trigger
                if self.state=='playing':
                    if self.show_params['has-child']=='yes':
                        self.play_child_signal=True
                        self.child_track_ref='pp-child-show'
                        # and stop the current track if its running
                        if self.player<>None:
                            self.player.input_pressed('stop')
                else:
                    if self.state=='waiting':
                        self.start_show()

            elif operation == 'pause':
                if self.player<>None:
                    self.player.input_pressed(operation)
                    
            #if the operation is omxplayer or mplayer runtime control then pass it to player if running
            elif operation[0:4]=='omx-' or operation[0:6]=='mplay-'or operation[0:5]=='uzbl-':
                if self.player<>None:
                    self.player.input_pressed(operation)



    def lookup_control(self,symbol,controls_list):
        for control in controls_list:
            if symbol == control[0]:
                return control[1]
        return ''


# ***************************
# Show sequencer
# ***************************

    def end_interval_timer(self):
        self.interval_timer_signal=True

    # callback from time of day scheduler
    def tod_start_callback(self):
         if self.state=='waiting' and self.show_params['trigger']in('time','time-quiet'):
            self.start_show()

    def tod_end_callback(self):
        if self.state=='playing' and self.show_params['trigger-end'] in ('time','duration'):
            self.end_trigger_signal=True
            if self.shower<>None:
                self.shower.input_pressed('stop')
            elif self.player<>None:
                self.player.input_pressed('stop')
                

    def stop(self,message):
        self.end_mediashow_signal=True
        if self.interval_timer<>None:
            self.canvas.after_cancel(self.interval_timer)

   
    def next(self):
        # stop track if running and set signal
        self.next_track_signal=True
        if self.shower<>None:
            self.shower.input_pressed("stop")
        else:
            if self.player<>None:
                self.player.input_pressed("stop")

    def previous(self):
        self.previous_track_signal=True
        if self.shower<>None:
            self.shower.input_pressed("stop")
        else:
            if self.player<>None:
                self.player.input_pressed("stop")
    
        
    # wait for trigger sets the state to waiting so that events can do a start show.    
    def wait_for_trigger(self):
        self.state='waiting'
        if self.show_ready_callback<>None:
            self.show_ready_callback()

        self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Waiting for trigger: "+ self.show_params['trigger'])
        
        if self.show_params['trigger']=="input":
            # blank screen waiting for trigger if auto, otherwise display something
            if self.show_params['progress']=="manual":
                text= self.resource('mediashow','m01')
            else:
                text= self.resource('mediashow','m02')
            self.display_message(self.canvas,'text',text,0,self.start_show)


        elif self.show_params['trigger']=="input-quiet":
            # blank screen waiting for trigger
            text = self.resource('mediashow','m10')
            self.display_message(self.canvas,'text',text,0,self.start_show)
            pass

        elif self.show_params['trigger'] in ('time','time-quiet'):
            # show next show notice
            quiet=3
            # if next show is this one display text
            next_show=self.tod.next_event_time()
            if next_show[quiet]==False:
                if next_show[1]=='tomorrow':
                    text = self.resource('mediashow','m09')
                else:
                    text = self.resource('mediashow','m08')                     
                text=text.replace('%tt',next_show[0])
                self.display_message(self.canvas,'text',text,0,self.start_show)  
            
        elif self.show_params['trigger']=="start":
            self.start_show()
            
        else:
            self.mon.err(self,"Unknown trigger: "+ self.show_params['trigger'])
            self.end('error',"Unknown trigger type")



  
    def start_show(self):
        self.state='playing'
        self.direction='forward'
        # self.canvas.delete(ALL)
        # start interval timer
        if self.show_params['repeat']=="interval" and self.show_params['repeat-interval']<>0:
            self.interval_timer_signal=False
            self.interval_timer=self.canvas.after(int(self.show_params['repeat-interval'])*1000,self.end_interval_timer)
            
        # start duration timer
        if self.show_params['trigger-end']=='duration':
            # print 'set alarm ', self.duration
            self.duration_timer = self.canvas.after(self.duration*1000,self.tod_end_callback)
        
        # and play the first track unless commanded otherwise
        if self.command=='backward':
            self.medialist.finish()
        else:
            self.medialist.start()
        self.play_selected_track(self.medialist.selected_track())
 
 
    def what_next(self):
        self.direction='forward'

        # end of show trigger caused by tod
        if self.end_trigger_signal==True:
            self.end_trigger_signal=False
            if self.top==True:
                self.state='waiting'
                self.wait_for_trigger()
            else:
                # not at top so stop the show
                self.end('normal','sub-show end time trigger')
        
        # user wants to end, wait for any shows or tracks to have ended then end show
        # probalby will get here with end_m set when player and shower has finished
        elif self.end_mediashow_signal==True:
            if self.player==None and self.shower==None:
                self.end_mediashow_signal=False
                self.end('normal',"show ended by user")

            
        #returning from a subshow needing to move onward 
        elif self.req_next=='do-next':
            self.req_next='nil'
            self.medialist.next(self.show_params['sequence'])
            self.play_selected_track(self.medialist.selected_track())
            
        #returning from a subshow needing to move backward 
        elif self.req_next=='do-previous':
            self.req_next='nil'
            self.direction='backward'
            self.medialist.previous(self.show_params['sequence'])
            self.play_selected_track(self.medialist.selected_track())         
               
        # user wants to play child
        elif self.play_child_signal == True:
            self.play_child_signal=False
            index = self.medialist.index_of_track(self.child_track_ref)
            if index >=0:
                #don't use select the track as need to preserve mediashow sequence.
                child_track=self.medialist.track(index)
                self.display_eggtimer(self.resource('mediashow','m07'))
                self.play_selected_track(child_track)
            else:
                self.mon.err(self,"Child show not found in medialist: "+ self.show_params['pp-child-show'])
                self.end('error',"child show not found in medialist")
        
        # skip to next track on user input
        elif self.next_track_signal==True:
            self.next_track_signal=False
            if self.medialist.at_end()==True:
                if  self.show_params['sequence']=="ordered" and self.show_params['repeat']=='oneshot' and self.top==False:
                    self.end('do-next',"Return from Sub Show")
                elif  self.show_params['sequence']=="ordered" and self.show_params['repeat']=='single-run' and self.top==False:
                    self.end('do-next',"Return from Sub Show")
                else:
                    self.medialist.next(self.show_params['sequence'])
                    self.play_selected_track(self.medialist.selected_track())               
            else:
                self.medialist.next(self.show_params['sequence'])
                self.play_selected_track(self.medialist.selected_track())
                
        # skip to previous track on user input
        elif self.previous_track_signal==True:
            self.previous_track_signal=False
            self.direction='backward'
            if self.medialist.at_start()==True:
                if  self.show_params['sequence']=="ordered" and self.show_params['repeat']=='oneshot' and self.top==False:
                    self.end('do-previous',"Return from Sub Show")
                elif  self.show_params['sequence']=="ordered" and self.show_params['repeat']=='single-run' and self.top==False:
                    self.end('do-previous',"Return from Sub Show")
                else:
                    self.medialist.previous(self.show_params['sequence'])
                    self.play_selected_track(self.medialist.selected_track())               
            else:
                self.medialist.previous(self.show_params['sequence'])              
                self.play_selected_track(self.medialist.selected_track())
        

        # track is finished and we are on auto        
        elif self.show_params['progress']=="auto":
            
            if self.medialist.at_end()==True:

                # oneshot
                if self.show_params['repeat']=='oneshot' and self.top==False:
                    self.end('normal',"End of Oneshot in subshow")
                    
                elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='oneshot' and self.top==True:
                    self.wait_for_trigger()

                # single run
                elif self.show_params['repeat']=='single-run' and self.top==True:
                   self.end('normal',"End of Single Run")

                elif self.show_params['repeat']=='single-run' and self.top==False:
                   self.end('do-next',"End of single run - Return from Sub Show")

                # repeating and waiting to restart 
                elif self.waiting_for_interval==True:
                    if self.interval_timer_signal==True:
                        self.interval_timer_signal=False
                        self.waiting_for_interval=False
                        self.start_show()
                    else:
                        self.poll_for_interval_timer=self.canvas.after(1000,self.what_next)
 
                elif self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])>0:
                    self.waiting_for_interval=True
                    self.poll_for_interval_timer=self.canvas.after(1000,self.what_next) 
                    
                #elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])==0:
                elif self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])==0:
                    self.medialist.next(self.show_params['sequence'])
                    self.play_selected_track(self.medialist.selected_track())

                # shuffling so there is no end condition
                elif self.show_params['sequence']=="shuffle":
                    self.medialist.next(self.show_params['sequence'])
                    self.play_selected_track(self.medialist.selected_track())
                    
                else:
                    self.mon.err(self,"Unhandled playing event: "+self.show_params['sequence'] +' with ' + self.show_params['repeat']+" of "+ self.show_params['repeat-interval'])
                    self.end('error',"Unhandled playing event")
                    
            else:
                self.medialist.next(self.show_params['sequence'])
                self.play_selected_track(self.medialist.selected_track())
                    
        # track has finished and we are on manual progress               
        elif self.show_params['progress']=="manual":
                    self.delete_eggtimer()
                    self.canvas.delete('pp-content')
                    if self.show_params['trigger-next']=='input':
                        self.display_eggtimer(self.resource('mediashow','m03'))
                    self.poll_for_continue_timer=self.canvas.after(2000,self.what_next)
                    
        else:
            #unhandled state
            self.mon.err(self,"Unhandled playing event: ")
            self.end('error',"Unhandled playing event")           


# ***************************
# Dispatching to Players/Shows 
# ***************************
   
    def ready_callback(self):
        self.delete_eggtimer()

    def play_selected_track(self,selected_track):
        """ selects the appropriate player from type field of the medialist and computes
              the parameters for that type
              selected track is a dictionary for the track/show
        """
        self.delete_eggtimer()
        if self.show_params['progress']=="manual":
            self.display_eggtimer(self.resource('mediashow','m04'))

        # is menu required
        if self.show_params['has-child']=="yes":
            self.enable_child=True
        else:
            self.enable_child=False

        #dispatch track by type
        self.player=None
        self.shower=None
        track_type = selected_track['type']
        self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Track type is: "+ track_type)
        if track_type=="video":
            # create a videoplayer
            track_file=self.complete_path(selected_track)
            self.player=VideoPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile)
            self.player.play(track_file,
                                        self.showlist,
                                        self.end_player,
                                        self.ready_callback,
                                        enable_menu=self.enable_child)
  
        elif track_type=="audio":
            # create a audioplayer
            track_file=self.complete_path(selected_track)
            self.player=AudioPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile)
            self.player.play(track_file,
                                        self.showlist,
                                        self.end_player,
                                        self.ready_callback,
                                        enable_menu=self.enable_child)
 
        elif track_type=="web":
            # create a browser
            track_file=self.complete_path(selected_track)
            self.player=BrowserPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile)
            self.player.play(track_file,
                                        self.showlist,
                                        self.end_player,
                                        self.ready_callback,
                                        enable_menu=self.enable_child)
  


 
        elif track_type=="image":
            track_file=self.complete_path(selected_track)
            # images played from menus don't have children
            self.player=ImagePlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile)
            self.player.play(track_file,
                                    self.showlist,
                                    self.end_player,
                                    self.ready_callback,
                                    enable_menu=self.enable_child)
                                    
        elif track_type=="message":
            # bit odd because MessagePlayer is used internally to display text. 
            text=selected_track['text']
            self.player=MessagePlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile)
            self.player.play(text,
                                    self.showlist,
                                    self.end_player,
                                    self.ready_callback,
                                    enable_menu=self.enable_child
                                    )

 
        elif track_type=="show":
            # get the show from the showlist
            index = self.showlist.index_of_show(selected_track['sub-show'])
            if index >=0:
                self.showlist.select(index)
                selected_show=self.showlist.selected_show()
            else:
                self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show'])
                self.end('error',"Unknown show")
                
            if selected_show['type']=="mediashow":    
                self.shower= MediaShow(selected_show,
                                                               self.root,
                                                                self.canvas,
                                                                self.showlist,
                                                               self.pp_dir,
                                                                self.pp_home,
                                                                self.pp_profile)
                print "Starting MediaShow: {0}".format(selected_track['sub-show'])
                self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command=self.direction)

            elif selected_show['type']=="liveshow":    
                self.shower= LiveShow(selected_show,
                                                                self.root,
                                                                self.canvas,
                                                                self.showlist,
                                                                self.pp_dir,
                                                                self.pp_home,
                                                                self.pp_profile)
                self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil')

            elif selected_show['type']=="radiobuttonshow":
                self.shower= RadioButtonShow(selected_show,
                                                         self.root,
                                                        self.canvas,
                                                        self.showlist,
                                                        self.pp_dir,
                                                        self.pp_home,
                                                        self.pp_profile)
                self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil')

            elif selected_show['type']=="hyperlinkshow":
                self.shower= HyperlinkShow(selected_show,
                                                       self.root,
                                                        self.canvas,
                                                        self.showlist,
                                                       self.pp_dir,
                                                        self.pp_home,
                                                        self.pp_profile)
                self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil')
            
            elif selected_show['type']=="menu":
                self.shower= MenuShow(selected_show,
                                                        self.root,
                                                        self.canvas,
                                                        self.showlist,
                                                          self.pp_dir,
                                                        self.pp_home,
                                                        self.pp_profile)
                self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil')
                
            else:
                self.mon.err(self,"Unknown Show Type: "+ selected_show['type'])
                self.end('error'"Unknown show type")  
            
        else:
            self.mon.err(self,"Unknown Track Type: "+ track_type)
            self.end('error',"Unknown track type")            


    def end_player(self,reason,message):
        self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Returned from player with message: "+ message)
        self.player=None
        self.req_next='nil'
        if reason in("killed","error"):
            self.end(reason,message)
        else:
            # elif>else move to what-next?
            if self.show_params['progress']=="manual":
                self.display_eggtimer(self.resource('mediashow','m05'))
                self.req_next=reason
                self.what_next()
            else:
                self.req_next=reason
                self.what_next()
                

    def end_shower(self,show_id,reason,message):
        self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Returned from shower with message: "+ message)
        self.shower=None
        self.req_next='nil'
        if reason in("killed","error"):
            self.end(reason,message)
        else:
            if self.show_params['progress']=="manual":
                self.display_eggtimer(self.resource('mediashow','m06'))
                self.req_next=reason
                self.what_next() 
            else:
                self.req_next=reason
                self.what_next() 



# ***************************
# end of show 
# ***************************

    def end(self,reason,message):
        self.end_mediashow_signal=False
        self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Ending Mediashow")
        self.tidy_up()
        self.end_callback(self.show_id,reason,message)
        self=None
        return

    def tidy_up(self):
        #clear outstanding time of day events for this show
        # self.tod.clear_times_list(id(self))
        if self.poll_for_continue_timer<>None:
                self.canvas.after_cancel(self.poll_for_continue_timer)
                self.poll_for_continue_timer=None
        if self.poll_for_interval_timer<>None:
                self.canvas.after_cancel(self.poll_for_interval_timer)
                self.poll_for_interval_timer=None
        if self.interval_timer<>None:
            self.canvas.after_cancel(self.interval_timer)
            self.interval_timer=None
        if self.duration_timer<>None:
            self.canvas.after_cancel(self.duration_timer)
            self.duration_timer=None


# ***************************
# displaying things
# ***************************
    
    def display_eggtimer(self,text):
        self.canvas.create_text(int(self.canvas['width'])/2,
                                              int(self.canvas['height'])/2,
                                                  text= text,
                                                  fill='white',
                                                  font="Helvetica 20 bold",
                                                tag='pp-eggtimer')
        self.canvas.update_idletasks( )


    def delete_eggtimer(self):
        self.canvas.delete('pp-eggtimer')
        self.canvas.update_idletasks( )


    # used to display internal messages in situations where a medialist entry could not be used.
    def display_message(self,canvas,source,content,duration,_display_message_callback):
            self.display_message_callback=_display_message_callback
            tp={'duration':duration,'message-colour':'white','message-font':'Helvetica 20 bold','background-colour':'',
                'message-justify':'left','background-image':'','show-control-begin':'','show-control-end':'',
                'animate-begin':'','animate-clear':'','animate-end':'','message-x':'','message-y':'',
                'display-show-background':'no','display-show-text':'no','show-text':'','track-text':'',
                'plugin':''}
            self.player=MessagePlayer(self.show_id,self.root,canvas,tp,tp,self.pp_dir,self.pp_home,self.pp_profile)
            self.player.play(content,self.showlist,self.display_message_end,None,False)

    def   display_message_end(self,reason,message):
        self.player=None
        if reason in ('error','killed'):
            self.end(reason,message)
        else:
            self.display_message_callback()



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

    def calculate_duration(self,line):
        fields=line.split(':')
        if len(fields)==1:
            secs=fields[0]
            minutes='0'
            hours='0'
        if len(fields)==2:
            secs=fields[1]
            minutes=fields[0]
            hours='0'
        if len(fields)==3:
            secs=fields[2]
            minutes=fields[1]
            hours=fields[0]
        self.duration=3600*long(hours)+60*long(minutes)+long(secs)
        return ''

    def resource(self,section,item):
        value=self.rr.get(section,item)
        if value==False:
            self.mon.err(self, "resource: "+section +': '+ item + " not found" )
            self.terminate("error")
        else:
            return value
        
    def complete_path(self,selected_track):
        #  complete path of the filename of the selected entry
        track_file = selected_track['location']
        if track_file<>'' and track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Track to play is: "+ track_file)
        return track_file     
Пример #6
0
class MenuShow:
    """ Displays a menu with optional hint below it. User can traverse the menu and
              select a track using key or button presses.
        Interface:
         * play - displays the menu and selects the first entry
         * key_pressed, button_pressed - receives user events passes them to a Player if a track is playing,
                otherwise actions them with _next, _previous, _play_selected_track, _end
         Optional display of eggtimer by means of Players ready_callback
         Supports imageplayer, videoplayer,messagplayer,audioplayer,menushow,mediashow
         Destroys itself on exit
    """

# *********************
# external interface
# ********************

    def __init__(self,
                            show_params,
                            canvas,
                            showlist,
                            pp_home,
                            pp_profile):
        """ canvas - the canvas that the menu is to be written on
            show - the name of the configuration dictionary section for the menu
            showlist  - the showlist
            pp_home - Pi presents data_home directory
            pp_profile - Pi presents profile directory"""
        
        self.mon=Monitor()
        self.mon.on()
        
        #instantiate arguments
        self.show_params=show_params
        self.showlist=showlist
        self.canvas=canvas
        self.pp_home=pp_home
        self.pp_profile=pp_profile

        # open resources
        self.rr=ResourceReader()
        
        # init variables
        self.drawn  = None
        self.player=None
        self.shower=None
        self.menu_timeout_running=None
        self.error=False



    def play(self,show_id,end_callback,ready_callback=None,top=False,command='nil'):
        """ displays the menu 
              end_callback - function to be called when the menu exits
              ready_callback - callback when menu is ready to display (not used)
              top is True when the show is top level (run from [start])
        """
        
        #instantiate arguments
        self.show_id=show_id
        self.end_callback=end_callback
        self.ready_callback=ready_callback
        self.top=top
        self.command=command

        # check  data files are available.
        self.menu_file = self.pp_profile + "/" + self.show_params['medialist']
        if not os.path.exists(self.menu_file):
            self.mon.err(self,"Medialist file not found: "+ self.menu_file)
            self._end('error',"Medialist file not found")
        
        #create a medialist for the menu and read it.
        self.medialist=MediaList()
        if self.medialist.open_list(self.menu_file,self.showlist.sissue()) == False:
            self.mon.err(self,"Version of medialist different to Pi Presents")
            self._end('error',"Version of medialist different to Pi Presents")
           
        if self.show_params['has-background']=="yes":
            background_index=self.medialist.index_of_track ('pp-menu-background')
            if background_index>=0:
                self.menu_img_file = self.complete_path(self.medialist.track(background_index))
                if not os.path.exists(self.menu_img_file):
                    self.mon.err(self,"Menu background file not found: "+ self.menu_img_file)
                    self._end('error',"Menu background file not found")
            else:
                self.mon.err(self,"Menu background not found in medialist")
                self._end('error',"Menu background not found")

        self.egg_timer=None

        #start timeout alarm if required
        if int(self.show_params['timeout'])<>0:
            self.menu_timeout_running=self.canvas.after(int(self.show_params['timeout'])*1000,self._timeout_menu)
        
        if self.ready_callback<>None:
            self.ready_callback()
        
        self.canvas.delete(ALL)
        
        # display background image
        if self.show_params['has-background']=="yes":
            self._display_background()
 
       #display the list of video titles
        self._display_video_titles()

        # display instructions (hint)
        self.canvas.create_text(int(self.canvas['width'])/2,
                                int(self.canvas['height']) - int(self.show_params['hint-y']),
                                text=self.show_params['hint-text'],
                                fill=self.show_params['hint-colour'],
                                font=self.show_params['hint-font'])
        self.canvas.update( )



   # respond to key presses.
    def key_pressed(self,key_name):
        self.mon.log(self,"received key: " + key_name)
        if self.show_params['disable-controls']=='yes':
            return 
        if key_name=='':
            pass
        
        elif key_name=='escape':
            # if next lower show eor player is running pass down to stop bottom level
            # ELSE stop this show if not at top
            if self.shower<>None:
                self.shower.key_pressed(key_name)
            elif self.player<>None:
                self.player.key_pressed(key_name)
            else:
                # not at top so stop the show
                if  self.top == False:
                    self._end('normal',"exit from stop command")
                else:
                    pass
      
        elif key_name in ('up','down'):
        # if child or sub-show running and is a show pass down
        # if  child not running - move
            if self.shower<>None:
                self.shower.key_pressed(key_name)
            else:
                if self.player==None:
                    if key_name=='up':
                        self._previous()
                    else:
                        self._next()
                
        elif key_name=='return':
            # if child running and is show - pass down
            # if no track already running  - play
            if self.shower<>None:
                self.shower.key_pressed(key_name)
            else:
                if self.player==None:
                    self._play_selected_track(self.medialist.selected_track())

        elif key_name in ('p',' '):
            # pass down if show or track running.
            if self.shower<>None:
                self.shower.key_pressed(key_name)
            elif self.player<>None:
                self.player.key_pressed(key_name)
 
 
    def button_pressed(self,button,edge):
        if button=='play': self.key_pressed("return")
        elif  button =='up': self.key_pressed("up")
        elif button=='down': self.key_pressed("down")
        elif button=='stop': self.key_pressed("escape")
        elif button=='pause': self.key_pressed('p')

        
    # kill or error
    def terminate(self,reason):
        if self.shower<>None:
            self.mon.log(self,"sent terminate to shower")
            self.shower.terminate(reason)
        elif self.player<>None:
            self.mon.log(self,"sent terminate to player")
            self.player.terminate(reason)
        else:
            self._end(reason,'terminated without terminating shower or player')



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

# *********************
# language resources
# *********************

    def resource(self,section,item):
        value=self.rr.get(section,item)
        if value==False:
            self.mon.err(self, "resource: "+section +': '+ item + " not found" )
            # timers may be running so need terminate
            self.terminate("error")
        else:
            return value


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

    def _timeout_menu(self):
        self._end('normal','menu timeout')
        return
        
    
    # finish the player for killing, error or normally
    # this may be called directly sub/child shows or players are not running
    # if they might be running then need to call terminate.
    
    def _end(self,reason,message):
        # self.canvas.delete(ALL)
        # self.canvas.update_idletasks( )
        self.mon.log(self,"Ending menushow: "+ self.show_params['show-ref'])  
        if self.menu_timeout_running<>None:
            self.canvas.after_cancel(self.menu_timeout_running)
            self.menu_timeout_running=None
        self.end_callback(self.show_id,reason,message)
        self=None
        return


    def _next(self):     
        self._highlight_menu_entry(self.menu_index,False)
        self.medialist.next('ordered')
        if self.menu_index==self.menu_length-1:
            self.menu_index=0
        else:
            self.menu_index+=1
        self._highlight_menu_entry(self.menu_index,True)     


    def _previous(self):   
        self._highlight_menu_entry(self.menu_index,False)
        if self.menu_index==0:
            self.menu_index=self.menu_length-1
        else:
            self.menu_index-=1
        self.medialist.previous('ordered')
        self._highlight_menu_entry(self.menu_index,True)    


# *********************
# Dispatching to Players
# *********************

    def complete_path(self,selected_track):
        #  complete path of the filename of the selected entry
        track_file = selected_track['location']
        if track_file<>'' and track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Track to play is: "+ track_file)
        return track_file     
         


    def _play_selected_track(self,selected_track):
        """ selects the appropriate player from type field of the medialist and computes
              the parameters for that type
              selected track is a dictionary for the track/show
        """
         #remove menu and show working.....        

        if self.menu_timeout_running<>None:
            self.canvas.after_cancel(self.menu_timeout_running)
            self.menu_timeout_running=None
        self.canvas.delete(ALL)
        self._display_eggtimer(self.resource('menushow','m01'))
    
        # dispatch track by type
        self.player=None
        self.shower=None
        track_type = selected_track['type']
        self.mon.log(self,"Track type is: "+ track_type)
        
        if track_type=="video":
            # create a videoplayer
            track_file=self.complete_path(selected_track)
            self.player=VideoPlayer(self.show_id,self.canvas,self.pp_home,self.show_params,selected_track)
            self.player.play(track_file,
                                        self._end_player,
                                        self._delete_eggtimer,
                                        enable_menu=False)
                                        
        elif track_type=="audio":
            # create a audioplayer
            track_file=self.complete_path(selected_track)
            self.player=AudioPlayer(self.show_id,self.canvas,self.pp_home,self.show_params,selected_track)
            self.player.play(track_file,
                                        self._end_player,
                                        self._delete_eggtimer,
                                        enable_menu=False)
                                        
        elif track_type=="image":
            # images played from menus don't have children
            track_file=self.complete_path(selected_track)
            self.player=ImagePlayer(self.show_id,self.canvas,self.pp_home,self.show_params,selected_track)
            self.player.play(track_file,
                                    self._end_player,
                                    self._delete_eggtimer,
                                    enable_menu=False,
                                    )
                                    
        elif track_type=="message":
            # bit odd because MessagePlayer is used internally to display text. 
            text=selected_track['text']
            self.player=MessagePlayer(self.show_id,self.canvas,self.pp_home,self.show_params,selected_track)
            self.player.play(text,
                                    self._end_player,
                                    self._delete_eggtimer,
                                    enable_menu=False
                                    )
 
        elif track_type=="show":
            # get the show from the showlist
            index = self.showlist.index_of_show(selected_track['sub-show'])
            if index >=0:
                self.showlist.select(index)
                selected_show=self.showlist.selected_show()
            else:
                self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show'])
                self._end("Unknown show")
            
            if selected_show['type']=="mediashow":    
                self.shower= MediaShow(selected_show,
                                                                self.canvas,
                                                                self.showlist,
                                                                self.pp_home,
                                                                self.pp_profile)
                self.shower.play(self.show_id,self._end_shower,top=False,command='nil')

            elif selected_show['type']=="liveshow":    
                self.shower= LiveShow(selected_show,
                                                                self.canvas,
                                                                self.showlist,
                                                                self.pp_home,
                                                                self.pp_profile)
                self.shower.play(self.show_id,self.end_shower,top=False,command='nil')

            elif selected_show['type']=="menu": 
                self.shower= MenuShow(selected_show,
                                                        self.canvas,
                                                        self.showlist,
                                                        self.pp_home,
                                                        self.pp_profile)
                self.shower.play(self.show_id,self._end_shower,top=False,command='nil')                    
            else:
                self.mon.err(self,"Unknown Show Type: "+ selected_show['type'])
                self._end("Unknown show type")  
                
        else:
            self.mon.err(self,"Unknown Track Type: "+ track_type)
            self._end("Unknown track type")
    
    # callback from when player ends
    def _end_player(self,reason,message):
        self.mon.log(self,"Returned from player with message: "+ message)
        self.player=None
        if reason in("killed","error"):
            self._end(reason,message)
        self._display_eggtimer(self.resource('menushow','m02'))
        self._what_next(message)

    # callback from when shower ends
    def _end_shower(self,show_id,reason,message):
        self.mon.log(self,"Returned from shower with message: "+ message)
        self.shower=None
        if message in ("killed","error"):
            self._end(reason,message)
        self._display_eggtimer(self.resource('menushow','m03'))
        self._what_next(message)  
   

     # at the end of a track just re-display the menu with the original callback from the menu       
    def _what_next(self,message):
        self.mon.log(self,"Re-displaying menu")
        self.play(self.show_id,self.end_callback,top=self.top)



# *********************
# Displaying things
# *********************

    def _display_background(self):
        pil_menu_img=PIL.Image.open(self.menu_img_file)
        # adjust brightness and rotate (experimental)
        # enh=PIL.ImageEnhance.Brightness(pil_menu_img)
        # pil_menu_img=enh.enhance(0.1)
        # pil_menu_img=pil_menu_img.rotate(45)
        self.menu_background = PIL.ImageTk.PhotoImage(pil_menu_img)
        self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                      int(self.canvas['height'])/2,
                                      image=self.menu_background,
                                      anchor=CENTER)


    def _display_video_titles(self):
        self.menu_length=1
        self.menu_entry_id=[]
        x=int(self.show_params['menu-x'])
        y=int(self.show_params['menu-y'])
        self.medialist.start()
        while True:
            id=self.canvas.create_text(x,y,anchor=NW,
                                       text="* "+self.medialist.selected_track()['title'],
                                       fill=self.show_params['entry-colour'],
                                       font=self.show_params['entry-font'])
            self.menu_entry_id.append(id)
            y=y + int(self.show_params['menu-spacing'])
            if self.medialist.at_end():
                break
            self.menu_length+=1
            self.medialist.next('ordered')
            
        # select and highlight the first entry
        self.medialist.start()
        self.menu_index=0
        self._highlight_menu_entry(self.menu_index,True)
        # self.medialist.print_list()

    def _highlight_menu_entry(self,index,state):
        if state==True:
            self.canvas.itemconfig(self.menu_entry_id[index],fill=self.show_params['entry-select-colour'])
        else:
            self.canvas.itemconfig(self.menu_entry_id[index],fill=self.show_params['entry-colour'])
    
    
    def _display_eggtimer(self,text):
        self.egg_timer=self.canvas.create_text(int(self.canvas['width'])/2,
                                              int(self.canvas['height'])/2,
                                                  text= text,
                                                  fill='white',
                                                  font="Helvetica 20 bold")
        self.canvas.update_idletasks( )


    def _delete_eggtimer(self):
        if self.egg_timer!=None:
            self.canvas.delete(self.egg_timer)
Пример #7
0
class MenuShow:
    """ Displays a menu with optional hint below it. User can traverse the menu and
              select a track using key or button presses.
        Interface:
         * play - displays the menu and selects the first entry
         * key_pressed, button_pressed - receives user events passes them to a Player if a track is playing,
                otherwise actions them with _next, _previous, _play_selected_track, _end
         Optional display of eggtimer by means of Players ready_callback
         Supports imageplayer, videoplayer,messagplayer,menushow,mediashow
         Destroys itself on exit
    """

# *********************
# external interface
# ********************

    def __init__(self,
                            show,
                            canvas,
                            showlist,
                            pp_home,
                            pp_profile):
        """ canvas - the canvas that the menu is to be written on
            show - the name of the configuration dictionary section for the menu
            cf  - the configuration object
            pp_home - Pi presents data_home directory
            pp_profile - Pi presents profile directory"""
        
        self.mon=Monitor()
        self.mon.on()
        
        #instantiate arguments
        self.show=show
        self.showlist=showlist
        self.canvas=canvas
        self.pp_home=pp_home
        self.pp_profile=pp_profile

        # open resources
        self.rr=ResourceReader()
        
        # init variables
        self.drawn  = None
        self.player=None
        self.shower=None
        self.menu_timeout_running=None
        self.error=False



    def play(self,end_callback,ready_callback=None,top=False,command='nil'):
        """ displays the menu 
              end_callback - function to be called when the menu exits
              ready_callback - callback when menu is ready to display (not used)
              top is True when the show is top level (run from [start])
        """
        
        #instantiate arguments
        self.end_callback=end_callback
        self.ready_callback=ready_callback
        self.top=top
        self.command=command

        
        # check  data files are available.
        self.menu_file = self.pp_profile + "/" + self.show['medialist']
        if not os.path.exists(self.menu_file):
            self.mon.err(self,"Medialist file not found: "+ self.menu_file)
            self._end('error',"Medialist file not found")
        
        #create a medialist for the menu and read it.
        self.medialist=MediaList()
        if self.medialist.open_list(self.menu_file,self.showlist.sissue()) == False:
            self.mon.err(self,"Version of medialist different to Pi Presents")
            self._end('error',"Version of medialist different to Pi Presents")
           
        if self.show['has-background']=="yes":
            background_index=self.medialist.index_of_track ('pp-menu-background')
            if background_index>=0:
                self.menu_img_file = self.complete_path(self.medialist.track(background_index))
                if not os.path.exists(self.menu_img_file):
                    self.mon.err(self,"Menu background file not found: "+ self.menu_img_file)
                    self._end('error',"Menu background file not found")
            else:
                self.mon.err(self,"Menu background not found in medialist")
                self._end('error',"Menu background not found")

        #start timeout alarm if required
        if int(self.show['timeout'])<>0:
            self.menu_timeout_running=self.canvas.after(int(self.show['timeout'])*1000,self._timeout_menu)
        
        if self.ready_callback<>None:
            self.ready_callback()
        
        self.canvas.delete(ALL)
        
        # display background image
        if self.show['has-background']=="yes":
            self._display_background()
 
       #display the list of video titles
        self._display_video_titles()

        # display instructions (hint)
        self.canvas.create_text(int(self.canvas['width'])/2,
                                int(self.canvas['height']) - int(self.show['hint-y']),
                                text=self.show['hint-text'],
                                fill=self.show['hint-colour'],
                                font=self.show['hint-font'])
        self.canvas.update( )



   # respond to key presses.
    def key_pressed(self,key_name):
        self.mon.log(self,"received key: " + key_name)
        
        if key_name=='':
            pass
        
        elif key_name=='escape':
            # if next lower show eor player is running pass down to stop bottom level
            # ELSE stop this show if not at top
            if self.shower<>None:
                self.shower.key_pressed(key_name)
            elif self.player<>None:
                self.player.key_pressed(key_name)
            else:
                # not at top so stop the show
                if  self.top == False:
                    self._end('normal',"exit from stop command")
                else:
                    pass
      
        elif key_name in ('up','down'):
        # if child or sub-show running and is a show pass down
        # if  child not running - move
            if self.shower<>None:
                self.shower.key_pressed(key_name)
            else:
                if self.player==None:
                    if key_name=='up':
                        self._previous()
                    else:
                        self._next()
                
        elif key_name=='return':
            # if child running and is show - pass down
            # if no track already running  - play
            if self.shower<>None:
                self.shower.key_pressed(key_name)
            else:
                if self.player==None:
                    self._play_selected_track(self.medialist.selected_track())

        elif key_name in ('p',' '):
            # pass down if show or track running.
            if self.shower<>None:
                self.shower.key_pressed(key_name)
            elif self.player<>None:
                self.player.key_pressed(key_name)
 
 
    def button_pressed(self,button,edge):
        if button=='play': self.key_pressed("return")
        elif  button =='up': self.key_pressed("up")
        elif button=='down': self.key_pressed("down")
        elif button=='stop': self.key_pressed("escape")
        elif button=='pause': self.key_pressed('p')

        
    # kill or error
    def terminate(self,reason):
        if self.shower<>None:
            self.mon.log(self,"sent terminate to shower")
            self.shower.terminate(reason)
        elif self.player<>None:
            self.mon.log(self,"sent terminate to player")
            self.player.terminate(reason)
        else:
            self._end(reason,'terminated without terminating shower or player')



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

# *********************
# language resources
# *********************

    def resource(self,section,item):
        value=self.rr.get(section,item)
        if value==False:
            self.mon.err(self, "resource: "+section +': '+ item + " not found" )
            # timers may be running so need terminate
            self.terminate("error")
        else:
            return value


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

    def _timeout_menu(self):
        self._end('normal','menu timeout')
        return
        
    
    # finish the player for killing, error or normally
    # this may be called directly sub/child shows or players are not running
    # if they might be running then need to call terminate.
    
    def _end(self,reason,message):
        self.canvas.delete(ALL)
        self.canvas.update_idletasks( )
        self.mon.log(self,"Ending menushow: "+ self.show['show-ref'])  
        if self.menu_timeout_running<>None:
            self.canvas.after_cancel(self.menu_timeout_running)
            self.menu_timeout_running=None
        self.end_callback(reason,message)
        self=None
        return


    def _next(self):     
        self._highlight_menu_entry(self.menu_index,False)
        self.medialist.next()
        if self.menu_index==self.menu_length-1:
            self.menu_index=0
        else:
            self.menu_index+=1
        self._highlight_menu_entry(self.menu_index,True)     


    def _previous(self):   
        self._highlight_menu_entry(self.menu_index,False)
        if self.menu_index==0:
            self.menu_index=self.menu_length-1
        else:
            self.menu_index-=1
        self.medialist.previous()
        self._highlight_menu_entry(self.menu_index,True)    


# *********************
# Dispatching to Players
# *********************

    def complete_path(self,selected_track):
        #  complete path of the filename of the selected entry
        track_file = selected_track['location']
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Track to play is: "+ track_file)
        return track_file     
         


    def _play_selected_track(self,selected_track):
        """ selects the appropriate player from type field of the medialist and computes
              the parameters for that type
              selected track is a dictionary for the track/show
        """
         #remove menu and show working.....        
        self.canvas.delete(ALL)
        if self.menu_timeout_running<>None:
            self.canvas.after_cancel(self.menu_timeout_running)
            self.menu_timeout_running=None
        self._display_eggtimer(self.resource('menushow','m01'))
    
        # dispatch track by type
        self.player=None
        self.shower=None
        track_type = selected_track['type']
        self.mon.log(self,"Track type is: "+ track_type)
        
        if track_type=="video":
            # create a videoplayer
            track_file=self.complete_path(selected_track)
            self.player=VideoPlayer(self.canvas,self.show,selected_track)
            self.player.play(track_file,
                                        self._end_player,
                                        self._delete_eggtimer,
                                        enable_menu=False)
                                        
        elif track_type=="image":
            # images played from menus don't have children
            enable_child=False
            track_file=self.complete_path(selected_track)
            self.player=ImagePlayer(self.canvas,self.show,selected_track)
            self.player.play(track_file,
                                    self._end_player,
                                    self._delete_eggtimer,
                                    enable_menu=enable_child,
                                    )
                                    
        elif track_type=="message":
            # bit odd because MessagePlayer is used internally to display text. 
            text=selected_track['text']
            self.player=MessagePlayer(self.canvas,self.show,selected_track)
            self.player.play(text,
                                    self._end_player,
                                    self._delete_eggtimer,
                                    enable_menu=False
                                    )
 
        elif track_type=="show":
            # get the show from the showlist
            index = self.showlist.index_of_show(selected_track['sub-show'])
            if index >=0:
                self.showlist.select(index)
                selected_show=self.showlist.selected_show()
            else:
                self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show'])
                self._end("Unknown show")
            
            if selected_show['type']=="mediashow":    
                self.shower= MediaShow(selected_show,
                                                                self.canvas,
                                                                self.showlist,
                                                                self.pp_home,
                                                                self.pp_profile)
                self.shower.play(self._end_shower,top=False,command='nil')

            elif selected_show['type']=="liveshow":    
                self.shower= LiveShow(selected_show,
                                                                self.canvas,
                                                                self.showlist,
                                                                self.pp_home,
                                                                self.pp_profile)
                self.shower.play(self.end_shower,top=False,command='nil')

            elif selected_show['type']=="menu": 
                self.shower= MenuShow(selected_show,
                                                        self.canvas,
                                                        self.showlist,
                                                        self.pp_home,
                                                        self.pp_profile)
                self.shower.play(self._end_shower,top=False,command='nil')                    
            else:
                self.mon.err(self,"Unknown Show Type: "+ selected_show['type'])
                self._end("Unknown show type")  
                
        else:
            self.mon.err(self,"Unknown Track Type: "+ track_type)
            self._end("Unknown track type")
    
    # callback from when player ends
    def _end_player(self,reason,message):
        self.mon.log(self,"Returned from player with message: "+ message)
        self.player=None
        if reason in("killed","error"):
            self._end(reason,message)
        self._display_eggtimer(self.resource('menushow','m02'))
        self._what_next(message)

    # callback from when shower ends
    def _end_shower(self,reason,message):
        self.mon.log(self,"Returned from shower with message: "+ message)
        self.shower=None
        if message in ("killed","error"):
            self._end(reason,message)
        self._display_eggtimer(self.resource('menushow','m03'))
        self._what_next(message)  
   

     # at the end of a track just re-display the menu with the original callback from the menu       
    def _what_next(self,message):
        self.mon.log(self,"Re-displaying menu")
        self.play(self.end_callback,top=self.top)



# *********************
# Displaying things
# *********************

    def _display_background(self):
        pil_menu_img=PIL.Image.open(self.menu_img_file)
        # adjust brightness and rotate (experimental)
        # enh=PIL.ImageEnhance.Brightness(pil_menu_img)
        # pil_menu_img=enh.enhance(0.1)
        # pil_menu_img=pil_menu_img.rotate(45)
        self.menu_background = PIL.ImageTk.PhotoImage(pil_menu_img)
        self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                      int(self.canvas['height'])/2,
                                      image=self.menu_background,
                                      anchor=CENTER)


    def _display_video_titles(self):
        self.menu_length=1
        self.menu_entry_id=[]
        x=int(self.show['menu-x'])
        y=int(self.show['menu-y'])
        self.medialist.start()
        while True:
            id=self.canvas.create_text(x,y,anchor=NW,
                                       text="* "+self.medialist.selected_track()['title'],
                                       fill=self.show['entry-colour'],
                                       font=self.show['entry-font'])
            self.menu_entry_id.append(id)
            y=y + int(self.show['menu-spacing'])
            if self.medialist.at_end():
                break
            self.menu_length+=1
            self.medialist.next()
            
        # select and highlight the first entry
        self.medialist.start()
        self.menu_index=0
        self._highlight_menu_entry(self.menu_index,True)
        # self.medialist.print_list()

    def _highlight_menu_entry(self,index,state):
        if state==True:
            self.canvas.itemconfig(self.menu_entry_id[index],fill=self.show['entry-select-colour'])
        else:
            self.canvas.itemconfig(self.menu_entry_id[index],fill=self.show['entry-colour'])
    
    
    def _display_eggtimer(self,text):
        self.canvas.create_text(int(self.canvas['width'])/2,
                                              int(self.canvas['height'])/2,
                                                  text= text,
                                                  fill='white',
                                                  font="Helvetica 20 bold")
        self.canvas.update_idletasks( )


    def _delete_eggtimer(self):
            self.canvas.delete(ALL)
Пример #8
0
class RadioMediaShow(Show):
    def __init__(self, show_id, show_params, root, canvas, showlist, pp_dir,
                 pp_home, pp_profile, command_callback):

        # init the common bits
        Show.base__init__(self, show_id, show_params, root, canvas, showlist,
                          pp_dir, pp_home, pp_profile, command_callback)

        # instatiatate the screen driver - used only to access enable and hide click areas
        self.sr = ScreenDriver()

        # create an instance of PathManager -  only used to parse the links.
        self.path = PathManager()

        self.allowed_links = ('play', 'pause', 'exit', 'return', 'null',
                              'no-command', 'stop')
        # init variables
        self.links = []
        self.track_timeout_timer = None
        self.show_timeout_timer = None
        self.next_track_signal = False
        self.current_track_ref = ''
        self.req_next = ''

        self.controlsmanager = ControlsManager()

        # Init variables special to this show
        self.poll_for_interval_timer = None
        self.interval_timer_signal = False
        self.waiting_for_interval = False
        self.interval_timer = None
        self.duration_timer = None

        self.end_trigger_signal = False
        self.next_track_signal = False
        self.previous_track_signal = False
        self.play_child_signal = False
        self.error_signal = False
        self.show_timeout_signal = False

        self.req_next = 'nil'
        self.state = 'closed'

        self.count = 0
        self.interval = 0
        self.duration = 0
        self.controls_list = []
        self.enable_hint = True

    # def play(self,end_callback,show_ready_callback, direction_command,level,controls_list):
    #
    #     # use the appropriate medialist
    #     self.medialist=MediaList(self.show_params['sequence'])
    #
    #     GapShow.play(self,end_callback,show_ready_callback, direction_command,level,controls_list)

    def play(self, end_callback, show_ready_callback, parent_kickback_signal,
             level, controls_list):
        # use the appropriate medialist
        self.medialist = MediaList(self.show_params['sequence'])

        self.mon.newline(3)
        self.mon.trace(self, self.show_params['show-ref'])

        Show.base_play(self, end_callback, show_ready_callback,
                       parent_kickback_signal, level, controls_list)

        # unpack show parameters

        reason, message, self.show_timeout = Show.calculate_duration(
            self, self.show_params['show-timeout'])
        if reason == 'error':
            self.mon.err(
                self, 'ShowTimeout has bad time: ' +
                self.show_params['show-timeout'])
            self.end(
                'error', 'ShowTimeout has bad time: ' +
                self.show_params['show-timeout'])

        self.track_count_limit = int(self.show_params['track-count-limit'])

        reason, message, self.interval = Show.calculate_duration(
            self, self.show_params['interval'])
        if reason == 'error':
            self.mon.err(
                self, 'Interval has bad time: ' + self.show_params['interval'])
            self.end('error',
                     'Interval has bad time: ' + self.show_params['interval'])

        # delete eggtimer started by the parent
        if self.previous_shower is not None:
            self.previous_shower.delete_eggtimer()

        self.start_show()

    # respond to inputs
    def handle_input_event(self, symbol):
        Show.base_handle_input_event(self, symbol)
        #self.handle_input_event_this_show(symbol)

    def handle_input_event_this_show(self, symbol):
        # for radiobuttonshow the symbolic names are links to play tracks, also a limited number of in-track operations
        # find the first entry in links that matches the symbol and execute its operation
        #print 'radiomediashow ', symbol
        found, link_op, link_arg = self.path.find_link(symbol, self.links)
        #print 'input event', symbol, link_op
        if found is True:
            if link_op == 'play':
                #print 'playing ' + link_arg
                self.do_play(link_arg)

            elif link_op == 'exit':
                #exit the show
                self.exit()

            elif link_op == 'stop':
                self.stop_timers()
                if self.current_player is not None:
                    if self.current_track_ref == self.first_track_ref and self.level != 0:
                        # if quiescent then set signal to stop the show when track has stopped
                        self.user_stop_signal = True
                    self.current_player.input_pressed('stop')

            elif link_op == 'return':
                # return to the first track
                if self.current_track_ref != self.first_track_ref:
                    self.do_play(self.first_track_ref)

            # in-track operations
            elif link_op == 'pause':
                if self.current_player is not None:
                    self.current_player.input_pressed(link_op)

            elif link_op in ('no-command', 'null'):
                return

            elif link_op[0:4] == 'omx-' or link_op[0:6] == 'mplay-' or link_op[
                    0:5] == 'uzbl-':
                if self.current_player is not None:
                    self.current_player.input_pressed(link_op)

            else:
                self.mon.err(self, "unknown link command: " + link_op)
                self.end('error', "unknown link command: " + link_op)

    def do_play(self, track_ref):
        #print 'do_play ' + track_ref
        # if track_ref != self.current_track_ref:
        # cancel the show timeout when playing another track
        if self.show_timeout_timer is not None:
            self.canvas.after_cancel(self.show_timeout_timer)
            self.show_timeout_timer = None
        # print '\n NEED NEXT TRACK'
        #self.next_track_signal=True
        self.next_track_op = 'play'
        self.next_track_arg = track_ref
        self.play_child_signal = True
        if self.shower is not None:
            #print 'current_shower not none so stopping',self.mon.id(self.current_shower)
            self.shower.do_operation('stop')
        elif self.current_player is not None:
            #print 'current_player not none so stopping',self.mon.id(self.current_player), ' for' ,track_ref
            self.current_player.input_pressed('stop')
        else:
            return

# ***************************
# Show sequencing
# ***************************

    def start_show(self):
        # initial direction from parent show

        self.kickback_for_next_track = self.parent_kickback_signal
        # print '\n\ninital KICKBACK from parent', self.kickback_for_next_track

        # start duration timer
        if self.show_timeout != 0:
            # print 'set alarm ', self.show_timeout
            self.duration_timer = self.canvas.after(self.show_timeout * 1000,
                                                    self.show_timeout_stop)

        self.first_list = True

        # and start the first list of the show
        self.wait_for_trigger()

    def wait_for_trigger(self):

        # wait for trigger sets the state to waiting so that trigger events can do a start_list.
        self.state = 'waiting'

        self.mon.log(
            self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
            ": Waiting for trigger: " + self.show_params['trigger-start-type'])

        if self.show_params['trigger-start-type'] == "input":

            #close the previous track to display admin message
            Show.base_shuffle(self)
            Show.base_track_ready_callback(self, False)
            Show.display_admin_message(self,
                                       self.show_params['trigger-wait-text'])

        elif self.show_params['trigger-start-type'] == "input-persist":
            if self.first_list == True:
                #first time through track list so play the track without waiting to get to end.
                self.first_list = False
                self.start_list()
            else:
                #wait for trigger while displaying previous track
                pass

        elif self.show_params['trigger-start-type'] == "start":
            # don't close the previous track to give seamless repeat of the show
            self.start_list()

        else:
            self.mon.err(
                self,
                "Unknown trigger: " + self.show_params['trigger-start-type'])
            self.end(
                'error',
                "Unknown trigger: " + self.show_params['trigger-start-type'])

    def start_list(self):
        # starts the list or any repeat having waited for trigger first.
        self.state = 'playing'

        # initialise track counter for the list
        self.track_count = 0

        # start interval timer
        self.interval_timer_signal = False
        if self.interval != 0:
            self.interval_timer = self.canvas.after(self.interval * 1000,
                                                    self.end_interval_timer)

        #get rid of previous track in order to display the empty message
        if self.medialist.display_length() == 0:
            Show.base_shuffle(self)
            Show.base_track_ready_callback(self, False)
            Show.display_admin_message(self, self.show_params['empty-text'])
            self.wait_for_not_empty()
        else:
            self.not_empty()

    def wait_for_not_empty(self):
        if self.medialist.display_length() == 0:
            # list is empty retry after 5 secs
            self.canvas.after(5000, self.wait_for_not_empty)
        else:
            Show.delete_admin_message(self)
            self.not_empty()

    def not_empty(self):
        #get first or last track depending on direction
        # print 'use direction for start or end of list', self.kickback_for_next_track
        if self.kickback_for_next_track is True:
            self.medialist.finish()
        else:
            self.medialist.start()
        self.start_load_show_loop(self.medialist.selected_track())

# ***************************
# Track load/show loop
# ***************************

# track playing loop starts here

    def start_load_show_loop(self, selected_track):

        # uncomment the next line to write stats for every track
        # Show.write_stats(self,'play a track',self.show_params,selected_track)

        # shuffle players
        Show.base_shuffle(self)

        self.delete_eggtimer()

        # is child track required
        if self.show_params['child-track-ref'] != '':
            self.enable_child = True
        else:
            self.enable_child = False

        # read the show links. Track links will  be added by ready_callback
        # needs to be done in show loop as each track adds different links to the show links
        if self.show_params['disable-controls'] == 'yes':
            self.links = []
        else:
            reason, message, self.links = self.path.parse_links(
                self.show_params['links'], self.allowed_links)
            if reason == 'error':
                self.mon.err(self, message + " in show")
                self.end('error', message + " in show")

        # load the track or show
        # params - track,enable_menu
        enable = self.enable_child & self.enable_hint
        Show.base_load_track_or_show(self, selected_track,
                                     self.what_next_after_load,
                                     self.end_shower, enable)

    # track has loaded so show it.
    def what_next_after_load(self, status, message):
        self.mon.log(
            self, 'Show Id ' + str(self.show_id) +
            ' load complete with status: ' + status + '  message: ' + message)
        if self.current_player.play_state == 'load-failed':
            self.error_signal = True
            self.what_next_after_showing()

        else:
            if self.terminate_signal is True or self.exit_signal is True or self.user_stop_signal is True:
                self.what_next_after_showing()
            else:
                self.mon.trace(self, ' - showing track')
                self.current_player.show(self.track_ready_callback,
                                         self.finished_showing,
                                         self.closed_after_showing)

    def finished_showing(self, reason, message):
        self.sr.hide_click_areas(self.controls_list)
        if self.current_player.play_state == 'show-failed':
            self.error_signal = True
        else:
            self.req_next = 'finished-player'
        # showing has finished with 'pause at end', showing the next track will close it after next has started showing
        self.mon.trace(self, ' - pause at end ')
        self.mon.log(
            self, "pause at end of showing track with reason: " + reason +
            ' and message: ' + message)
        self.what_next_after_showing()

    def closed_after_showing(self, reason, message):
        self.sr.hide_click_areas(self.controls_list)
        if self.current_player.play_state == 'show-failed':
            self.error_signal = True
        else:
            self.req_next = 'closed-player'
        # showing has finished with closing of player but track instance is alive for hiding the x_content
        self.mon.trace(self, ' - closed')
        self.mon.log(
            self, "Closed after showing track with reason: " + reason +
            ' and message: ' + message)
        self.what_next_after_showing()

    # subshow or child show has ended
    def end_shower(self, show_id, reason, message):
        self.mon.log(
            self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
            ': Returned from shower with ' + reason + ' ' + message)
        self.sr.hide_click_areas(self.controls_list)
        self.req_next = reason
        Show.base_end_shower(self)
        self.what_next_after_showing()

    def pretty_what_next_after_showing_state(self):
        state = '\n* terminate signal ' + str(self.terminate_signal)
        state += '\n* error signal ' + str(self.error_signal)
        state += '\n* req_next used to indicate subshow reports an error ' + self.req_next
        state += '\n* exit signal ' + str(self.exit_signal)
        state += '\n* show timeout  signal ' + str(self.show_timeout_signal)
        state += '\n* user stop  signal ' + str(self.user_stop_signal)
        state += '\n* previous track  signal ' + str(
            self.previous_track_signal)
        state += '\n* next track  signal ' + str(self.next_track_signal)
        state += '\n* kickback from subshow ' + str(
            self.subshow_kickback_signal)
        return state + '\n'

    def what_next_after_showing(self):
        self.mon.trace(self, self.pretty_what_next_after_showing_state())

        self.track_count += 1
        # set false when child rack is to be played
        self.enable_hint = True

        # first of all deal with conditions that do not require the next track to be shown
        # some of the conditions can happen at any time, others only when a track is closed or at pause_at_end

        # need to terminate
        if self.terminate_signal is True:
            self.terminate_signal = False
            self.stop_timers()
            # set what to do after closed or unloaded
            self.ending_reason = 'killed'
            Show.base_close_or_unload(self)

        elif self.error_signal == True or self.req_next == 'error':
            self.error_signal = False
            self.req_next = ''
            self.stop_timers()
            # set what to do after closed or unloaded
            self.ending_reason = 'error'
            Show.base_close_or_unload(self)

        # used for exiting show from other shows, time of day, external etc.
        elif self.exit_signal is True:
            self.exit_signal = False
            self.stop_timers()
            self.ending_reason = 'exit'
            Show.base_close_or_unload(self)

        #  show timeout
        elif self.show_timeout_signal is True:
            self.show_timeout_signal = False
            self.stop_timers()
            self.ending_reason = 'user-stop'
            Show.base_close_or_unload(self)

        # user wants to stop the show
        elif self.user_stop_signal is True:
            self.user_stop_signal = False
            self.stop_timers()
            self.ending_reason = 'user-stop'
            Show.base_close_or_unload(self)

    # interval > 0. If last track has finished we are waiting for interval timer before ending the list
    # note: if medialist finishes after interval is up then this route is used to start trigger.
        elif self.waiting_for_interval is True:
            # interval_timer_signal set by alarm clock started in start_list
            if self.interval_timer_signal is True:
                self.interval_timer_signal = False
                self.waiting_for_interval = False
                # print 'RECEIVED INTERVAL TIMER SIGNAL'
                if self.show_params['repeat'] == 'repeat':
                    self.wait_for_trigger()
                else:
                    self.stop_timers()
                    self.ending_reason = 'user-stop'
                    Show.base_close_or_unload(self)
            else:
                self.poll_for_interval_timer = self.canvas.after(
                    1000, self.what_next_after_showing)

        # has content of list been changed (replaced if it has, used for content of livelist)
        # causes it to go to wait_for_trigger and start_list
        #not sure why need clos_and_unload here ???????
        elif self.medialist.replace_if_changed() is True:
            self.ending_reason = 'change-medialist'
            # print 'CHANGE REQ'
            Show.base_close_or_unload(self)

        # otherwise consider operation that might show the next track
        else:
            # setup default direction for next track as normally goes forward unless kicked back
            self.kickback_for_next_track = False
            # print 'init direction to False at begin of what_next_after showing', self.kickback_for_next_track
            # end trigger from input, or track count
            if self.end_trigger_signal is True or (self.track_count_limit > 0
                                                   and self.track_count
                                                   == self.track_count_limit):
                self.end_trigger_signal = False
                # repeat so test start trigger
                if self.show_params['repeat'] == 'repeat':
                    self.stop_timers()
                    # print 'END TRIGGER restart'
                    self.wait_for_trigger()
                else:
                    # single run so exit show
                    if self.level != 0:
                        self.kickback_for_next_track = False
                        self.subshow_kickback_signal = False
                        self.end('normal',
                                 "End of single run - Return from Sub Show")
                    else:
                        # end of single run and at top - exit the show
                        self.stop_timers()
                        self.ending_reason = 'user-stop'
                        Show.base_close_or_unload(self)

            # user wants to play child
            elif self.play_child_signal is True:
                self.play_child_signal = False
                # index = self.medialist.index_of_track(self.child_track_ref)
                # if index >=0:
                #     # don't use select the track as need to preserve mediashow sequence for returning from child
                #     child_track=self.medialist.track(index)
                #     Show.write_stats(self,'play child',self.show_params,child_track)
                #     self.display_eggtimer()
                #     self.enable_hint=False
                #     self.start_load_show_loop(child_track)
                # else:
                #     self.mon.err(self,"Child not found in medialist: "+ self.child_track_ref)
                #     self.ending_reason='error'
                #     Show.base_close_or_unload(self)

                # play user selected track # HOOK !
                self.current_track_ref = self.next_track_arg
                #print 'what next - next track signal is True so load ', self.current_track_ref
                index = self.medialist.index_of_track(self.current_track_ref)
                if index >= 0:
                    # don't use select the track as not using selected_track in radiobuttonshow
                    # and load it
                    Show.write_stats(self, 'play', self.show_params,
                                     self.medialist.track(index))
                    self.display_eggtimer()
                    self.enable_hint = False
                    self.start_load_show_loop(self.medialist.track(index))
                else:
                    self.mon.err(
                        self, "next track not found in medialist: " +
                        self.current_track_ref)
                    self.end(
                        'error', "next track not found in medialist: " +
                        self.current_track_ref)
                    Show.base_close_or_unload(self)

            # skip to next track on user input or after subshow
            elif self.next_track_signal is True:
                #print 'skip forward test' ,self.subshow_kickback_signal
                if self.next_track_signal is True or self.subshow_kickback_signal is False:
                    self.next_track_signal = False
                    self.kickback_for_next_track = False

                    if self.medialist.at_end() is True:
                        # medialist_at_end can give false positive for shuffle
                        if self.show_params[
                                'sequence'] == "ordered" and self.show_params[
                                    'repeat'] == 'repeat':
                            self.wait_for_trigger()
                        elif self.show_params[
                                'sequence'] == "ordered" and self.show_params[
                                    'repeat'] == 'single-run':
                            if self.level != 0:
                                self.kickback_for_next_track = False
                                self.subshow_kickback_signal = False
                                # print 'end subshow skip forward test, self. direction is ' ,self.kickback_for_next_track
                                self.end('normal', "Return from Sub Show")
                            else:
                                # end of single run and at top - exit the show
                                self.stop_timers()
                                self.ending_reason = 'user-stop'
                                Show.base_close_or_unload(self)
                        else:
                            # shuffling  - just do next track
                            self.kickback_for_next_track = False
                            self.medialist.next(self.show_params['sequence'])
                            self.start_load_show_loop(
                                self.medialist.selected_track())
                    else:
                        # not at end just do next track
                        self.medialist.next(self.show_params['sequence'])
                        self.start_load_show_loop(
                            self.medialist.selected_track())

            # skip to previous track on user input or after subshow
            elif self.previous_track_signal is True or self.subshow_kickback_signal is True:
                #print 'skip backward test, subshow kickback is' ,self.subshow_kickback_signal
                self.subshow_kickback_signal = False
                self.previous_track_signal = False
                self.kickback_for_next_track = True
                # medialist_at_start can give false positive for shuffle
                if self.medialist.at_start() is True:
                    # print 'AT START'
                    if self.show_params[
                            'sequence'] == "ordered" and self.show_params[
                                'repeat'] == 'repeat':
                        self.kickback_for_next_track = True
                        self.wait_for_trigger()
                    elif self.show_params[
                            'sequence'] == "ordered" and self.show_params[
                                'repeat'] == 'single-run':
                        if self.level != 0:
                            self.kickback_for_next_track = True
                            self.subshow_kickback_signal = True
                            # print 'end subshow skip forward test, self. direction is ' ,self.kickback_for_next_track
                            self.end('normal', "Return from Sub Show")
                        else:
                            # end of single run and at top - exit the show
                            self.stop_timers()
                            self.ending_reason = 'user-stop'
                            Show.base_close_or_unload(self)
                    else:
                        # shuffling  - just do previous track
                        self.kickback_for_next_track = True
                        self.medialist.previous(self.show_params['sequence'])
                        self.start_load_show_loop(
                            self.medialist.selected_track())
                else:
                    # not at end just do next track
                    self.medialist.previous(self.show_params['sequence'])
                    self.start_load_show_loop(self.medialist.selected_track())

            # AT END OF MEDIALIST
            elif self.medialist.at_end() is True:
                #print 'MEDIALIST AT END'

                # interval>0 and list finished so wait for the interval timer
                if self.show_params[
                        'sequence'] == "ordered" and self.interval > 0 and self.interval_timer_signal == False:
                    self.waiting_for_interval = True
                    # print 'WAITING FOR INTERVAL'
                    Show.base_shuffle(self)
                    Show.base_track_ready_callback(self, False)
                    self.poll_for_interval_timer = self.canvas.after(
                        200, self.what_next_after_showing)

                # interval=0
                #elif self.show_params['sequence'] == "ordered" and self.show_params['repeat'] == 'repeat' and self.show_params['trigger-end-type']== 'interval' and int(self.show_params['trigger-end-param']) == 0:
                #self.medialist.next(self.show_params['sequence'])
                # self.start_load_show_loop(self.medialist.selected_track())

                # shuffling so there is no end condition, get out of end test
                elif self.show_params['sequence'] == "shuffle":
                    self.medialist.next(self.show_params['sequence'])
                    self.start_load_show_loop(self.medialist.selected_track())

                # nothing special to do at end of list, just repeat or exit
                elif self.show_params['sequence'] == "ordered":
                    if self.show_params['repeat'] == 'repeat':
                        self.wait_for_trigger()
                    else:
                        # single run
                        # if not at top return to parent
                        if self.level != 0:
                            self.end('normal', "End of Single Run")
                        else:
                            # at top so close the show
                            self.stop_timers()
                            self.ending_reason = 'user-stop'
                            Show.base_close_or_unload(self)

                else:
                    self.mon.err(
                        self, "Unhandled playing event at end of list: " +
                        self.show_params['sequence'] + ' with ' +
                        self.show_params['repeat'] + " of " +
                        self.show_params['trigger-end-param'])
                    self.end(
                        'error', "Unhandled playing event at end of list: " +
                        self.show_params['sequence'] + ' with ' +
                        self.show_params['repeat'] + " of " +
                        self.show_params['trigger-end-param'])

            elif self.medialist.at_end() is False:
                # nothing special just do the next track
                self.medialist.next(self.show_params['sequence'])
                self.start_load_show_loop(self.medialist.selected_track())

            else:
                # unhandled state
                self.mon.err(
                    self, "Unhandled playing event: " +
                    self.show_params['sequence'] + ' with ' +
                    self.show_params['repeat'] + " of " +
                    self.show_params['trigger-end-param'])
                self.end(
                    'error', "Unhandled playing event: " +
                    self.show_params['sequence'] + ' with ' +
                    self.show_params['repeat'] + " of " +
                    self.show_params['trigger-end-param'])

# *********************
# Interface with other shows/players to reduce black gaps
# *********************

# # called just before a track is shown to remove the  previous track from the screen
# # and if necessary close it
# def track_ready_callback(self,enable_show_background):
#     self.delete_eggtimer()
#
#     # get control bindings for this show
#     # needs to be done for each track as track can override the show controls
#     if self.show_params['disable-controls'] == 'yes':
#         self.controls_list=[]
#     else:
#         reason,message,self.controls_list= self.controlsmanager.get_controls(self.show_params['controls'])
#         if reason=='error':
#             self.mon.err(self,message)
#             self.end('error',"error in controls: " + message)
#             return
#
#         # print 'controls',reason,self.show_params['controls'],self.controls_list
#         #merge controls from the track
#         controls_text=self.current_player.get_links()
#         reason,message,track_controls=self.controlsmanager.parse_controls(controls_text)
#         if reason == 'error':
#             self.mon.err(self,message + " in track: "+ self.current_player.track_params['track-ref'])
#             self.error_signal=True
#             self.what_next_after_showing()
#         self.controlsmanager.merge_controls(self.controls_list,track_controls)
#
#     # enable the click-area that are in the list of controls
#     self.sr.enable_click_areas(self.controls_list)
#     Show.base_track_ready_callback(self,enable_show_background)

# called just before a track is shown to remove the  previous track from the screen
# and if necessary close it

    def track_ready_callback(self, enable_show_background):
        self.delete_eggtimer()
        # print 'TRACK READY CALLBACK'
        # print 'previous is',self.mon.id(self.previous_player), self.next_track_signal

        #merge links from the track
        if self.show_params['disable-controls'] == 'yes':
            track_links = []
        else:
            reason, message, track_links = self.path.parse_links(
                self.current_player.get_links(), self.allowed_links)
            if reason == 'error':
                self.mon.err(
                    self, message + " in track: " +
                    self.current_player.track_params['track-ref'])
                self.req_next = 'error'
                self.what_next_after_showing()
        self.path.merge_links(self.links, track_links)
        # enable the click-area that are in the list of links
        self.sr.enable_click_areas(self.links)

        Show.base_track_ready_callback(self, enable_show_background)

    # callback from begining of a subshow, provide previous player to called show
    def subshow_ready_callback(self):
        return Show.base_subshow_ready_callback(self)

# *********************
# End the show
# *********************

    def end(self, reason, message):
        Show.base_end(self, reason, message)

    def stop_timers(self):
        # clear outstanding time of day events for this show
        # self.tod.clear_times_list(id(self))

        if self.poll_for_interval_timer is not None:
            self.canvas.after_cancel(self.poll_for_interval_timer)
            self.poll_for_interval_timer = None

        if self.interval_timer is not None:
            self.canvas.after_cancel(self.interval_timer)
            self.interval_timer = None

        if self.duration_timer is not None:
            self.canvas.after_cancel(self.duration_timer)
            self.duration_timer = None
Пример #9
0
class MediaShow:

    # *******************
    # External interface
    # ********************

    def __init__(self, show_params, canvas, showlist, pp_home, pp_profile):
        """ canvas - the canvas that the menu is to be written on
            show - the dictionary fo the show to be played
            pp_home - Pi presents data_home directory
            pp_profile - Pi presents profile directory
        """

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

        # instantiate arguments
        self.show_params = show_params
        self.showlist = showlist
        self.canvas = canvas
        self.pp_home = pp_home
        self.pp_profile = pp_profile

        # open resources
        self.rr = ResourceReader()

        # Init variables
        self.player = None
        self.shower = None
        self._poll_for_interval_timer = None
        self._poll_for_continue_timer = None
        self._waiting_for_interval = False
        self._interval_timer = None
        self.duration_timer = None
        self.error = False

        self._interval_timer_signal = False
        self._end_trigger_signal = False
        self._end_mediashow_signal = False
        self._next_track_signal = False
        self._previous_track_signal = False
        self._play_child_signal = False
        self._req_next = "nil"

        # create and instance of TimeOfDay scheduler so we can add events
        self.tod = TimeOfDay()

        self._state = "closed"

    def play(self, show_id, end_callback, ready_callback=None, top=False, command="nil"):

        """ displays the mediashow
              end_callback - function to be called when the menu exits
              ready_callback - callback when menu is ready to display (not used)
              top is True when the show is top level (run from [start])
        """

        # instantiate the arguments
        self.show_id = show_id
        self._end_callback = end_callback
        self._ready_callback = ready_callback
        self.top = top
        self.command = command
        self.mon.log(self, "Starting show: Id= " + str(self.show_id) + "  " + self.show_params["show-ref"])

        # check  data files are available.
        self.media_file = self.pp_profile + "/" + self.show_params["medialist"]
        if not os.path.exists(self.media_file):
            self.mon.err(self, "Medialist file not found: " + self.media_file)
            self._end("error", "Medialist file not found")

        # create a medialist for the mediashow and read it.
        self.medialist = MediaList()
        if self.medialist.open_list(self.media_file, self.showlist.sissue()) == False:
            self.mon.err(self, "Version of medialist different to Pi Presents")
            self._end("error", "Version of medialist different to Pi Presents")

        # set up the time of day triggers for the show
        if self.show_params["trigger"] in ("time", "time-quiet"):
            error_text = self.tod.add_times(
                self.show_params["trigger-input"], id(self), self.tod_start_callback, self.show_params["trigger"]
            )
            if error_text <> "":
                self.mon.err(self, error_text)
                self._end("error", error_text)

        if self.show_params["trigger-end"] == "time":
            # print self.show_params['trigger-end-time']
            error_text = self.tod.add_times(
                self.show_params["trigger-end-time"], id(self), self.tod_end_callback, "n/a"
            )
            if error_text <> "":
                self.mon.err(self, error_text)
                self._end("error", error_text)

        if self.show_params["trigger-end"] == "duration":
            error_text = self.calculate_duration(self.show_params["trigger-end-time"])
            if error_text <> "":
                self.mon.err(self, error_text)
                self._end("error", error_text)

        self._state = "closed"
        self.egg_timer = None
        self._wait_for_trigger()

    def calculate_duration(self, line):
        fields = line.split(":")
        if len(fields) == 1:
            secs = fields[0]
            minutes = "0"
            hours = "0"
        if len(fields) == 2:
            secs = fields[1]
            minutes = fields[0]
            hours = "0"
        if len(fields) == 3:
            secs = fields[2]
            minutes = fields[1]
            hours = fields[0]
        self.duration = 3600 * long(hours) + 60 * long(minutes) + long(secs)
        return ""

    # ********************************
    # Respond to external events
    # ********************************

    # respond to key presses
    def key_pressed(self, key_name):
        self.mon.log(self, "received key: " + key_name)
        if self.show_params["disable-controls"] == "yes":
            return
        if key_name == "":
            pass

        elif key_name == "escape":
            # if next lower show is running pass down to stop the show and lower level
            if self.shower <> None:
                self.shower.key_pressed(key_name)
            # if not at top stop the show
            else:
                if self.top == False:
                    self._end_mediashow_signal = True
                    # and if a track is running stop that first
                    if self.player <> None:
                        self.player.key_pressed(key_name)
                else:
                    # at top level in a manual presentation stop the track
                    if self.show_params["progress"] == "manual":
                        if self.player <> None:
                            self.player.key_pressed(key_name)

        elif key_name in ("up", "down"):
            # if child or sub-show is running and is a show pass to show, track does not use up/down
            # otherwise use keys for next or previous
            if self.shower <> None:
                self.shower.key_pressed(key_name)
            else:
                if key_name == "up" and self._state == "playing":
                    self._previous()
                else:
                    self._next()

        elif key_name == "return":
            # if child show or sub-show is running and is show - pass down- player does not use return
            # ELSE use Return to start child or to start the show if waiting
            if self.shower <> None:
                self.shower.key_pressed(key_name)
            else:
                if self._state == "playing":
                    if self.show_params["has-child"] == "yes":
                        self._play_child_signal = True
                        # and stop the current track if its running
                        if self.player <> None:
                            self.player.key_pressed("escape")
                else:
                    if self._state == "waiting":
                        self._start_show()

        elif key_name in ("p", " "):
            # pass down if show or track running.
            if self.shower <> None:
                self.shower.key_pressed(key_name)
            elif self.player <> None:
                self.player.key_pressed(key_name)

    def button_pressed(self, button, edge):
        # print 'mediashow button pressed', button
        if button == "play":
            self.key_pressed("return")
        elif button == "up":
            self.key_pressed("up")
        elif button == "down":
            self.key_pressed("down")
        elif button == "stop":
            self.key_pressed("escape")
        elif button == "pause":
            self.key_pressed("p")
        else:
            self.input_pressed(button)

    def input_pressed(self, xinput):
        # print self._state, self.show_params['trigger-next'], self.show_params['next-input']
        if (
            self._state == "waiting"
            and self.show_params["trigger"] == "GPIO"
            and xinput == self.show_params["trigger-input"]
        ):
            self.key_pressed("return")
        elif (
            self._state == "playing"
            and self.show_params["trigger-next"] == "GPIO"
            and xinput == self.show_params["next-input"]
        ):
            self.key_pressed("down")

    # callback from time of day scheduler
    def tod_start_callback(self):
        if self._state == "waiting" and self.show_params["trigger"] in ("time", "time-quiet"):
            self._start_show()

    def tod_end_callback(self):
        if self._state == "playing" and self.show_params["trigger-end"] in ("time", "duration"):
            self._end_trigger_signal = True
            if self.shower <> None:
                self.shower.key_pressed("escape")
            elif self.player <> None:
                self.player.key_pressed("escape")

    # kill or error
    def terminate(self, reason):
        if self.shower <> None:
            self.mon.log(self, "sent terminate to shower")
            self.shower.terminate(reason)
        elif self.player <> None:
            self.mon.log(self, "sent terminate to player")
            self.player.terminate(reason)
        else:
            self._end(reason, "terminated without terminating shower or player")

    def _tidy_up(self):
        # clear outstanding time of day events for this show
        self.tod.clear_times_list(id(self))
        if self._poll_for_continue_timer <> None:
            self.canvas.after_cancel(self._poll_for_continue_timer)
            self._poll_for_continue_timer = None
        if self._poll_for_interval_timer <> None:
            self.canvas.after_cancel(self._poll_for_interval_timer)
            self._poll_for_interval_timer = None
        if self._interval_timer <> None:
            self.canvas.after_cancel(self._interval_timer)
            self._interval_timer = None
        if self.duration_timer <> None:
            self.canvas.after_cancel(self.duration_timer)
            self.duration_timer = None

    def resource(self, section, item):
        value = self.rr.get(section, item)
        if value == False:
            self.mon.err(self, "resource: " + section + ": " + item + " not found")
            self.terminate("error")
        else:
            return value

    # ***************************
    # Do actions as a result of events
    # ***************************

    def _stop(self, message):
        self._end_mediashow_signal = True
        if self._interval_timer <> None:
            self.canvas.after_cancel(self._interval_timer)

    def _next(self):
        # stop track if running and set signal
        self._next_track_signal = True
        if self.shower <> None:
            self.shower.key_pressed("escape")
        else:
            if self.player <> None:
                self.player.key_pressed("escape")

    def _previous(self):
        self._previous_track_signal = True
        if self.shower <> None:
            self.shower.key_pressed("escape")
        else:
            if self.player <> None:
                self.player.key_pressed("escape")

    # ***************************
    # end of show functions
    # ***************************

    def _end(self, reason, message):
        self._end_mediashow_signal = False
        self.mon.log(self, "Ending Mediashow: " + self.show_params["show-ref"])
        self._tidy_up()
        self._end_callback(self.show_id, reason, message)
        self = None
        return

    # ***************************
    # Show sequencer
    # ***************************

    # wait for trigger sets the state to waiting so that key/button presses can do a start show.

    def _wait_for_trigger(self):
        self._state = "waiting"
        if self.ready_callback <> None:
            self.ready_callback()

        self.mon.log(self, "Waiting for trigger: " + self.show_params["trigger"])

        if self.show_params["trigger"] == "button":
            # blank screen waiting for trigger if auto, otherwise display something
            if self.show_params["progress"] == "manual":
                text = self.resource("mediashow", "m01")
            else:
                text = ""
            self.display_message(self.canvas, "text", text, 0, self._start_show)

        elif self.show_params["trigger"] == "GPIO":
            # blank screen waiting for trigger
            # text = self.resource('mediashow','m02')
            # self.display_message(self.canvas,'text',text,0,self._start_show)
            pass

        elif self.show_params["trigger"] in ("time", "time-quiet"):
            # show next show notice
            quiet = 3
            # if next show is this one display text
            next_show = self.tod.next_event_time()
            if next_show[quiet] == False:
                if next_show[1] == "tomorrow":
                    text = self.resource("mediashow", "m09")
                else:
                    text = self.resource("mediashow", "m08")
                text = text.replace("%tt", next_show[0])
                self.display_message(self.canvas, "text", text, 0, self._start_show)

        elif self.show_params["trigger"] == "start":
            self._start_show()

        else:
            self.mon.err(self, "Unknown trigger: " + self.show_params["trigger"])
            self._end("error", "Unknown trigger type")

    def _start_show(self):
        self._state = "playing"
        self._direction = "forward"
        # self.canvas.delete(ALL)
        # start interval timer
        if self.show_params["repeat"] == "interval" and self.show_params["repeat-interval"] <> 0:
            self._interval_timer_signal = False
            self._interval_timer = self.canvas.after(
                int(self.show_params["repeat-interval"]) * 1000, self._end_interval_timer
            )

        # start duration timer
        if self.show_params["trigger-end"] == "duration":
            # print 'set alarm ', self.duration
            self.duration_timer = self.canvas.after(self.duration * 1000, self.tod_end_callback)

        # and play the first track unless commanded otherwise
        if self.command == "backward":
            self.medialist.finish()
        else:
            self.medialist.start()
        self._play_selected_track(self.medialist.selected_track())

    def _what_next(self):
        self._direction = "forward"

        # end of show trigger
        if self._end_trigger_signal == True:
            self._end_trigger_signal = False
            if self.top == True:
                self._state = "waiting"
                self._wait_for_trigger()
            else:
                # not at top so stop the show
                self._end("normal", "sub-show end time trigger")

        # user wants to end, wait for any shows or tracks to have ended then end show
        elif self._end_mediashow_signal == True:
            if self.player == None and self.shower == None:
                self._end_mediashow_signal = False
                self._end("normal", "show ended by user")
            else:
                pass

        # returning from a subshow needing to move onward
        elif self._req_next == "do-next":
            self._req_next = "nil"
            self.medialist.next(self.show_params["sequence"])
            self._play_selected_track(self.medialist.selected_track())

        # returning from a subshow needing to move backward
        elif self._req_next == "do-previous":
            self._req_next = "nil"
            self._direction = "backward"
            self.medialist.previous(self.show_params["sequence"])
            self._play_selected_track(self.medialist.selected_track())

        # user wants to play child
        elif self._play_child_signal == True:
            self._play_child_signal = False
            index = self.medialist.index_of_track("pp-child-show")
            if index >= 0:
                # don't select the track as need to preserve mediashow sequence.
                child_track = self.medialist.track(index)
                self._display_eggtimer(self.resource("mediashow", "m07"))
                self._play_selected_track(child_track)
            else:
                self.mon.err(self, "Child show not found in medialist: " + self.show_params["pp-child-show"])
                self._end("error", "child show not found in medialist")

        # skip to next track on user input
        elif self._next_track_signal == True:
            self._next_track_signal = False
            if self.medialist.at_end() == True:
                if (
                    self.show_params["sequence"] == "ordered"
                    and self.show_params["repeat"] == "oneshot"
                    and self.top == False
                ):
                    self._end("do-next", "Return from Sub Show")
                else:
                    self.medialist.next(self.show_params["sequence"])
                    self._play_selected_track(self.medialist.selected_track())
            else:
                self.medialist.next(self.show_params["sequence"])
                self._play_selected_track(self.medialist.selected_track())

        # skip to previous track on user input
        elif self._previous_track_signal == True:
            self._previous_track_signal = False
            self._direction = "backward"
            if self.medialist.at_start() == True:
                if (
                    self.show_params["sequence"] == "ordered"
                    and self.show_params["repeat"] == "oneshot"
                    and self.top == False
                ):
                    self._end("do-previous", "Return from Sub Show")
                else:
                    self.medialist.previous(self.show_params["sequence"])
                    self._play_selected_track(self.medialist.selected_track())
            else:
                self.medialist.previous(self.show_params["sequence"])
                self._play_selected_track(self.medialist.selected_track())

        # track is finished and we are on auto
        elif self.show_params["progress"] == "auto":

            if self.medialist.at_end() == True:

                if (
                    self.show_params["sequence"] == "ordered"
                    and self.show_params["repeat"] == "oneshot"
                    and self.top == False
                ):
                    self._end("do-next", "Return from Sub Show")

                elif (
                    self.show_params["sequence"] == "ordered"
                    and self.show_params["repeat"] == "oneshot"
                    and self.top == True
                ):
                    self._wait_for_trigger()

                elif self._waiting_for_interval == True:
                    if self._interval_timer_signal == True:
                        self._interval_timer_signal = False
                        self._waiting_for_interval = False
                        self._start_show()
                    else:
                        self._poll_for_interval_timer = self.canvas.after(1000, self._what_next)

                elif (
                    self.show_params["sequence"] == "ordered"
                    and self.show_params["repeat"] == "interval"
                    and int(self.show_params["repeat-interval"]) > 0
                ):
                    self._waiting_for_interval = True
                    self._poll_for_interval_timer = self.canvas.after(1000, self._what_next)

                # elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])==0:
                elif self.show_params["repeat"] == "interval" and int(self.show_params["repeat-interval"]) == 0:
                    self.medialist.next(self.show_params["sequence"])
                    self._play_selected_track(self.medialist.selected_track())

                # shffling so there is no end condition
                elif self.show_params["sequence"] == "shuffle":
                    self.medialist.next(self.show_params["sequence"])
                    self._play_selected_track(self.medialist.selected_track())

                else:
                    self.mon.err(
                        self,
                        "Unhandled playing event: "
                        + self.show_params["sequence"]
                        + " with "
                        + self.show_params["repeat"]
                        + " of "
                        + self.show_params["repeat-interval"],
                    )
                    self._end("error", "Unhandled playing event")

            else:
                self.medialist.next(self.show_params["sequence"])
                self._play_selected_track(self.medialist.selected_track())

        # track has finished and we are on manual progress
        elif self.show_params["progress"] == "manual":
            self._delete_eggtimer()
            self.canvas.delete(ALL)
            if self.show_params["trigger-next"] == "button":
                self._display_eggtimer(self.resource("mediashow", "m03"))
            self._poll_for_continue_timer = self.canvas.after(2000, self._what_next)

        else:
            # unhandled state
            self.mon.err(self, "Unhandled playing event: ")
            self._end("error", "Unhandled playing event")

    def _end_interval_timer(self):
        self._interval_timer_signal = True

    # ***************************
    # Dispatching to Players/Shows
    # ***************************

    # used to display internal messages in situations where a medialist entry could be used.
    def display_message(self, canvas, source, content, duration, _display_message_callback):
        self._display_message_callback = _display_message_callback
        tp = {
            "duration": duration,
            "message-colour": "white",
            "message-font": "Helvetica 20 bold",
            "background-colour": "",
            "background-image": "",
        }
        self.player = MessagePlayer(self.show_id, canvas, self.pp_home, tp, tp)
        self.player.play(content, self._display_message_end, None)

    def _display_message_end(self, reason, message):
        self.player = None
        if reason in ("error", "killed"):
            self._end(reason, message)
        else:
            self._display_message_callback()

    def complete_path(self, selected_track):
        #  complete path of the filename of the selected entry
        track_file = selected_track["location"]
        if track_file <> "" and track_file[0] == "+":
            track_file = self.pp_home + track_file[1:]
        self.mon.log(self, "Track to play is: " + track_file)
        return track_file

    def _play_selected_track(self, selected_track):
        """ selects the appropriate player from type field of the medialist and computes
              the parameters for that type
              selected track is a dictionary for the track/show
        """
        # self.canvas.delete(ALL)
        if self.show_params["progress"] == "manual":
            self._display_eggtimer(self.resource("mediashow", "m04"))

        # is menu required
        if self.show_params["has-child"] == "yes":
            enable_child = True
        else:
            enable_child = False

        # dispatch track by type
        self.player = None
        self.shower = None
        track_type = selected_track["type"]
        self.mon.log(self, "Track type is: " + track_type)

        if track_type == "video":
            # create a videoplayer
            track_file = self.complete_path(selected_track)
            self.player = VideoPlayer(self.show_id, self.canvas, self.pp_home, self.show_params, selected_track)
            self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child)

        elif track_type == "audio":
            # create a audioplayer
            track_file = self.complete_path(selected_track)
            self.player = AudioPlayer(self.show_id, self.canvas, self.pp_home, self.show_params, selected_track)
            self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child)

        elif track_type == "image":
            track_file = self.complete_path(selected_track)
            # images played from menus don't have children
            self.player = ImagePlayer(self.show_id, self.canvas, self.pp_home, self.show_params, selected_track)
            self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child)

        elif track_type == "message":
            # bit odd because MessagePlayer is used internally to display text.
            text = selected_track["text"]
            self.player = MessagePlayer(self.show_id, self.canvas, self.pp_home, self.show_params, selected_track)
            self.player.play(text, self.end_player, self.ready_callback, enable_menu=enable_child)

        elif track_type == "show":
            # get the show from the showlist
            index = self.showlist.index_of_show(selected_track["sub-show"])
            if index >= 0:
                self.showlist.select(index)
                selected_show = self.showlist.selected_show()
            else:
                self.mon.err(self, "Show not found in showlist: " + selected_track["sub-show"])
                self._end("error", "Unknown show")

            if selected_show["type"] == "mediashow":
                self.shower = MediaShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile)
                self.shower.play(self.show_id, self.end_shower, top=False, command=self._direction)

            elif selected_show["type"] == "liveshow":
                self.shower = LiveShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile)
                self.shower.play(self.show_id, self.end_shower, top=False, command="nil")

            elif selected_show["type"] == "menu":
                self.shower = MenuShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile)
                self.shower.play(self.show_id, self.end_shower, top=False, command="nil")

            else:
                self.mon.err(self, "Unknown Show Type: " + selected_show["type"])
                self._end("error" "Unknown show type")

        else:
            self.mon.err(self, "Unknown Track Type: " + track_type)
            self._end("error", "Unknown track type")

    def ready_callback(self):
        self._delete_eggtimer()

    def end_player(self, reason, message):
        self._req_next = "nil"
        self.mon.log(self, " Show Id: " + str(self.show_id) + " Returned from player with message: " + message)
        self.player = None
        if reason in ("killed", "error"):
            self._end(reason, message)
        elif self.show_params["progress"] == "manual":
            self._display_eggtimer(self.resource("mediashow", "m05"))
            self._req_next = reason
            self._what_next()
        else:
            self._req_next = reason
            self._what_next()

    def end_shower(self, show_id, reason, message):
        self._req_next = "nil"
        self.mon.log(self, "Returned from shower with message: " + message)
        self.shower = None
        if reason in ("killed", "error"):
            self._end(reason, message)
        elif self.show_params["progress"] == "manual":
            self._display_eggtimer(self.resource("mediashow", "m06"))
            self._req_next = reason
            self._what_next()
        else:
            self._req_next = reason
            self._what_next()

    def _display_eggtimer(self, text):
        self.egg_timer = self.canvas.create_text(
            int(self.canvas["width"]) / 2,
            int(self.canvas["height"]) / 2,
            text=text,
            fill="white",
            font="Helvetica 20 bold",
        )
        self.canvas.update_idletasks()

    def _delete_eggtimer(self):
        if self.egg_timer != None:
            self.canvas.delete(self.egg_timer)
            self.canvas.update_idletasks()