Пример #1
0
class Show(object):

    # ******************************
    # init a show
    # ******************************

    def base__init__(self, show_id, show_params, root, canvas, showlist,
                     pp_dir, pp_home, pp_profile, command_callback):

        # instantiate arguments
        self.show_id = show_id
        self.show_params = show_params
        self.root = root
        self.show_canvas = canvas
        self.canvas = canvas['canvas-obj']
        self.show_canvas_x1 = canvas['show-canvas-x1']
        self.show_canvas_y1 = canvas['show-canvas-y1']
        self.show_canvas_x2 = canvas['show-canvas-x2']
        self.show_canvas_y2 = canvas['show-canvas-y2']
        self.show_canvas_width = canvas['show-canvas-width']
        self.show_canvas_height = canvas['show-canvas-height']
        self.show_canvas_centre_x = canvas['show-canvas-centre-x']
        self.show_canvas_centre_y = canvas['show-canvas-centre-y']
        self.showlist = showlist
        self.pp_dir = pp_dir
        self.pp_home = pp_home
        self.pp_profile = pp_profile
        self.command_callback = command_callback

        # init things that will then be reinitialised by derived classes
        self.medialist = None

        # set up logging
        self.mon = Monitor()
        self.mon.set_log_level(16)

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

        # create an  instance of showmanager so we can init child/subshows
        self.show_manager = ShowManager(self.show_id, self.showlist,
                                        self.show_params, self.root,
                                        self.show_canvas, self.pp_dir,
                                        self.pp_profile, self.pp_home)

        # init variables
        self.current_player = None
        self.previous_player = None
        self.shower = None
        self.previous_shower = None
        self.user_stop_signal = False
        self.exit_signal = False
        self.terminate_signal = False
        self.show_timeout_signal = False
        self.egg_timer = None
        self.admin_message = None
        self.ending_reason = ''
        self.background_obj = None
        self.background_file = ''
        self.level = 0
        self.subshow_kickback_signal = False
        self.kickback_for_next_track = False

        # get background image from profile.
        # print 'background', self.show_params['background-image']
        if self.show_params['background-image'] != '':
            self.background_file = self.show_params['background-image']

    def base_play(self, end_callback, show_ready_callback,
                  parent_kickback_signal, level, controls_list):
        """ starts the common parts of the show
              end_callback - function to be called when the show exits- callback gets last player of subshow
              show_ready_callback - callback at start to get previous_player
              top is True when the show is top level (run from [start] or by show command from another show)
              direction_command - 'forward' or 'backward' direction to play a subshow
        """
        # instantiate the arguments
        self.end_callback = end_callback
        self.show_ready_callback = show_ready_callback
        self.parent_kickback_signal = parent_kickback_signal
        self.level = level
        # not needed as controls list is not passed down to subshows.
        # self.controls_list=controls_list
        self.mon.trace(
            self,
            self.show_params['show-ref'] + ' at level ' + str(self.level))
        self.mon.log(
            self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
            ": Starting show")

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

        # read the medialist for the show
        if self.medialist.open_list(self.medialst_file,
                                    self.showlist.sissue()) is 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_ready_callback is not None:
            # get the previous player from calling show its stored in current because its going to be shuffled before use
            self.previous_shower, self.current_player = self.show_ready_callback(
            )
            self.mon.trace(
                self, ' - previous shower and player is ' +
                self.mon.pretty_inst(self.previous_shower) + ' ' +
                self.mon.pretty_inst(self.current_player))

        #load the show background
        reason, message = Show.base_load_show_background(self)
        if reason == 'error':
            self.mon.err(self, message)
            self.end('error', message)

    # dummy, must be overidden by derived class
    def subshow_ready_callback(self):
        self.mon.err(self, "subshow_ready_callback not overidden")
        # set what to do when closed or unloaded
        self.ending_reason = 'killed'
        Show.base_close_or_unload(self)

    def base_subshow_ready_callback(self):
        # callback from begining of a subshow, provide previous player to called show
        # used by show_ready_callback of called show
        # in the case of a menushow last track is always the menu
        self.mon.trace(
            self, ' -  sends ' + self.mon.pretty_inst(self.previous_player))
        return self, self.previous_player

    def base_shuffle(self):
        self.previous_player = self.current_player
        self.current_player = None
        self.mon.trace(
            self, ' - LOOP STARTS WITH current is: ' +
            self.mon.pretty_inst(self.current_player))
        self.mon.trace(
            self, '       -  previous is: ' +
            self.mon.pretty_inst(self.previous_player))

    def base_load_track_or_show(self, selected_track, loaded_callback,
                                end_shower_callback, enable_menu):
        track_type = selected_track['type']
        if track_type == "show":
            # get the show from the showlist
            index = self.showlist.index_of_show(selected_track['sub-show'])
            if index < 0:
                self.mon.err(
                    self, "Show not found in showlist: " +
                    selected_track['sub-show'])
                self.end('error',
                         'show not in showlist: ' + selected_track['sub-show'])
            else:
                self.showlist.select(index)
                selected_show = self.showlist.selected_show()
                self.shower = self.show_manager.init_subshow(
                    self.show_id, selected_show, self.show_canvas)
                self.mon.trace(
                    self, ' - show is: ' + self.mon.pretty_inst(self.shower) +
                    ' ' + selected_show['show-ref'])
                if self.shower is None:
                    self.mon.err(self,
                                 "Unknown Show Type: " + selected_show['type'])
                    self.terminate_signal = True
                    self.what_next_after_showing()
                else:
                    # empty controls list as not used, pleases landscape
                    # print 'send direction to subshow from show',self.kickback_for_next_track
                    # Show.base_withdraw_show_background(self)
                    self.shower.play(end_shower_callback,
                                     self.subshow_ready_callback,
                                     self.kickback_for_next_track,
                                     self.level + 1, [])
        else:
            # dispatch track by type
            self.mon.log(
                self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
                ": Track type is: " + track_type)

            self.current_player = self.base_init_selected_player(
                selected_track)
            #menu has no track file
            if selected_track['type'] == 'menu':
                track_file = ''

            # messageplayer passes the text not a file name
            elif selected_track['type'] == 'message':
                track_file = selected_track['text']
            else:
                track_file = self.base_complete_path(
                    selected_track['location'])

            self.mon.trace(self, ' - track is: ' + track_file)
            self.mon.trace(
                self, ' - current_player is: ' +
                self.mon.pretty_inst(self.current_player))
            self.current_player.load(track_file,
                                     loaded_callback,
                                     enable_menu=enable_menu)

    # DUMMY, must be overidden by derived class
    def what_next_after_showing(self):
        self.mon.err(self, "what_next_after showing not overidden")
        # set what to do when closed or unloaded
        self.ending_reason = 'killed'
        Show.base_close_or_unload(self)

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

        if track_type == "image":
            return ImagePlayer(self.show_id, self.showlist, self.root,
                               self.show_canvas, self.show_params,
                               selected_track, self.pp_dir, self.pp_home,
                               self.pp_profile, self.end,
                               self.command_callback)

        elif track_type == "video":
            return VideoPlayer(self.show_id, self.showlist, self.root,
                               self.show_canvas, self.show_params,
                               selected_track, self.pp_dir, self.pp_home,
                               self.pp_profile, self.end,
                               self.command_callback)

        elif track_type == "audio":
            return AudioPlayer(self.show_id, self.showlist, self.root,
                               self.show_canvas, self.show_params,
                               selected_track, self.pp_dir, self.pp_home,
                               self.pp_profile, self.end,
                               self.command_callback)

        elif track_type == "web" and self.show_params['type'] not in (
                'artmediashow', 'artliveshow'):
            return BrowserPlayer(self.show_id, self.showlist, self.root,
                                 self.show_canvas, self.show_params,
                                 selected_track, self.pp_dir, self.pp_home,
                                 self.pp_profile, self.end,
                                 self.command_callback)

        elif track_type == "message":
            return MessagePlayer(self.show_id, self.showlist, self.root,
                                 self.show_canvas, self.show_params,
                                 selected_track, self.pp_dir, self.pp_home,
                                 self.pp_profile, self.end,
                                 self.command_callback)

        elif track_type == "menu":
            return MenuPlayer(self.show_id, self.showlist, self.root,
                              self.show_canvas, self.show_params,
                              selected_track, self.pp_dir, self.pp_home,
                              self.pp_profile, self.end, self.command_callback)

        else:
            return None

    # DUMMY, must be overidden by derived class
    def track_ready_callback(self, track_background):
        self.mon.err(self, "track_ready_callback not overidden")
        # set what to do when closed or unloaded
        self.ending_reason = 'killed'
        Show.base_close_or_unload(self)

    # called just before a track is shown to remove the  previous track from the screen
    # and if necessary close it
    def base_track_ready_callback(self, enable_show_background):
        self.mon.trace(self, '')
        # show the show background done for every track but quick operation
        if enable_show_background is True:
            self.base_show_show_background()
        else:
            self.base_withdraw_show_background()
        # !!!!!!!!! withdraw the background from the parent show
        if self.previous_shower != None:
            self.previous_shower.base_withdraw_show_background()
        # close the player from the previous track
        if self.previous_player is not None:
            self.mon.trace(
                self, ' - hiding previous: ' +
                self.mon.pretty_inst(self.previous_player))
            self.previous_player.hide()
            # print 'Not None  - previous state is',self.previous_player.get_play_state()
            if self.previous_player.get_play_state() == 'showing':
                # print 'showing so closing previous'
                # showing or frozen
                self.mon.trace(
                    self, ' - closing previous: ' +
                    self.mon.pretty_inst(self.previous_player))
                self.previous_player.close(self._base_closed_callback_previous)
            else:
                self.mon.trace(self, ' - previous is none\n')
                self.previous_player = None
        self.canvas.update_idletasks()

    def _base_closed_callback_previous(self, status, message):
        self.mon.trace(
            self, ' -  previous is None  - was: ' +
            self.mon.pretty_inst(self.previous_player))
        self.previous_player = None

    # used by end_shower to get the last track of the subshow
    def base_end_shower(self):
        self.mon.trace(self, ' -  returned back to level: ' + str(self.level))
        # get the previous subshow and last track it played
        self.previous_shower, self.current_player = self.shower.base_subshow_ended_callback(
        )
        if self.previous_shower != None:
            self.subshow_kickback_signal = self.shower.subshow_kickback_signal
            # print 'get subshow kickback from subshow',self.subshow_kickback_signal
            self.previous_shower.base_withdraw_show_background()
            self.base_show_show_background()
        self.previous_player = None
        self.mon.trace(
            self, '- get previous_player from subshow: ' +
            self.mon.pretty_inst(self.current_player))
        self.shower = None

    # close or unload the current player when ending the show
    def base_close_or_unload(self):
        self.mon.trace(self, self.mon.pretty_inst(self.current_player))
        # need to test for None because player may be made None by subshow lower down the stack for terminate
        if self.current_player is not None:
            self.mon.trace(self, self.current_player.get_play_state())
            if self.current_player.get_play_state() in ('loaded', 'showing',
                                                        'show-failed'):
                if self.current_player.get_play_state() == 'loaded':
                    self.mon.trace(
                        self,
                        ' - unloading current from: ' + self.ending_reason)
                    self.current_player.unload()
                else:
                    self.mon.trace(
                        self, ' - closing current from: ' + self.ending_reason)
                    self.current_player.close(None)
            self._wait_for_end()
        else:
            # current_player is None because closed further down show stack
            self.mon.trace(
                self, ' - show ended with current_player=None because: ' +
                self.ending_reason)

            # if exiting pipresents then need to close previous show else get memotry leak
            # if not exiting pipresents the keep previous so it can be closed when showing the next track
            # print 'CURRENT PLAYER IS NONE' ,self.ending_reason
            if self.ending_reason == 'killed':
                self.base_close_previous()

            elif self.ending_reason == 'error':
                self.base_close_previous()

            elif self.ending_reason == 'exit':
                self.end('normal', "show quit by exit command")

            elif self.ending_reason == 'user-stop':
                self.end('normal', "show quit by stop operation")

            else:
                self.mon.fatal(
                    self, "Unhandled ending_reason: " + self.ending_reason)
                self.end('error',
                         "Unhandled ending_reason: " + self.ending_reason)

    def _base_closed_callback_current(self, status, message):
        self.mon.trace(
            self, ' current is None  - was: ' +
            self.mon.pretty_inst(self.current_player))

    # wait for unloading or closing to complete then end
    def _wait_for_end(self):
        self.mon.trace(self, self.mon.pretty_inst(self.current_player))
        if self.current_player is not None:
            self.mon.trace(
                self,
                ' - play state is ' + self.current_player.get_play_state())
            if self.current_player.play_state not in ('unloaded', 'closed',
                                                      'load-failed'):  ####
                self.canvas.after(50, self._wait_for_end)
            else:
                self.mon.trace(
                    self, ' - current closed ' +
                    self.mon.pretty_inst(self.current_player) + ' ' +
                    self.ending_reason)

                #why is some of thsi different to close and unload????????????? perhaps because current_player isn't none, just closed
                if self.ending_reason == 'killed':
                    self.current_player.hide()
                    self.current_player = None
                    self.base_close_previous()

                elif self.ending_reason == 'error':
                    self.current_player.hide()
                    self.current_player = None
                    self.base_close_previous()

                elif self.ending_reason == 'exit':
                    self.current_player.hide()
                    self.current_player = None
                    self.base_close_previous()

                elif self.ending_reason == 'change-medialist':
                    self.current_player.hide()
                    self.current_player = None
                    # self.base_close_previous()
                    # go to start of list via wait for trigger.
                    self.wait_for_trigger()

                elif self.ending_reason == 'show-timeout':
                    self.current_player.hide()
                    self.current_player = None
                    self.end('normal', "show timeout")

                elif self.ending_reason == 'user-stop':
                    if self.level != 0:
                        self.end('normal', "show quit by stop operation")
                    else:
                        self.current_player.hide()
                        self.current_player = None
                        self.base_close_previous()

                else:
                    self.mon.fatal(
                        self, "Unhandled ending_reason: " + self.ending_reason)
                    self.end('error',
                             "Unhandled ending_reason: " + self.ending_reason)
        else:
            self.mon.trace(
                self, ' - current is None ' +
                self.mon.pretty_inst(self.current_player) + ' ' +
                self.ending_reason)

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

# dummy, normally overidden by derived class

    def end(self, reason, message):
        self.mon.err(self, "end not overidden")
        self.base_end('error', message)

    def base_end(self, reason, message):
        self.base_withdraw_show_background()
        self.base_delete_show_background()
        self.mon.trace(
            self, ' at level ' + str(self.level) + '\n - Current is ' +
            self.mon.pretty_inst(self.current_player) + '\n - Previous is ' +
            self.mon.pretty_inst(self.previous_player) + '\n with reason' +
            reason + '\n\n')
        self.mon.log(
            self, self.show_params['show-ref'] + ' Show Id: ' +
            str(self.show_id) + ": Ending Show")
        self.end_callback(self.show_id, reason, message)
        self = None

    def base_subshow_ended_callback(self):
        # called by end_shower of a parent show  to get the last track of the subshow and the subshow
        self.mon.trace(
            self, ' -  returns ' + self.mon.pretty_inst(self.current_player))
        return self, self.current_player

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

    def base_close_previous(self):
        self.mon.trace(self, '')
        # close the player from the previous track
        if self.previous_player is not None:
            self.mon.trace(
                self, ' - previous not None ' +
                self.mon.pretty_inst(self.previous_player))
            if self.previous_player.get_play_state() == 'showing':
                # showing or frozen
                self.mon.trace(
                    self, ' - closing previous ' +
                    self.mon.pretty_inst(self.previous_player))
                self.previous_player.close(self._base_close_previous_callback)
            else:
                self.mon.trace(self, 'previous is not showing')
                self.previous_player.hide()
                self.previous_player = None
                self.end(self.ending_reason, '')
        else:
            self.mon.trace(self, ' - previous is None')
            self.end(self.ending_reason, '')

    def _base_close_previous_callback(self, status, message):
        self.mon.trace(
            self, ' -  previous is None  - was ' +
            self.mon.pretty_inst(self.previous_player))
        self.previous_player.hide()
        self.previous_player = None
        self.end(self.ending_reason, '')

    # exit received from external source
    def base_exit(self):
        self.mon.log(
            self, self.show_params['show-ref'] + ' ' + str(self.show_id) +
            ": Exit received")
        self.mon.trace(self, '')
        # set signal to exit the show when all  sub-shows and players have ended
        self.exit_signal = True
        # then stop subshow or tracks.
        if self.shower is not None:
            self.shower.exit()
        elif self.current_player is not None:
            self.current_player.input_pressed('stop')
        else:
            self.end('normal', 'exit by ShowManager')

    # show timeout callback received
    def base_show_timeout_stop(self):
        self.mon.trace(self, '')
        # set signal to exit the show when all  sub-shows and players have ended
        self.show_timeout_signal = True
        # then stop and shows or tracks.
        if self.shower is not None:
            self.shower.show_timeout_stop()
        elif self.current_player is not None:
            self.current_player.input_pressed('stop')
        else:
            self.end('normal', 'stopped by Show Timeout')

    # dummy, normally overidden by derived class
    def terminate(self):
        self.mon.err(self, "terminate not overidden")
        self.base_end('error', "terminate not overidden")

    # terminate Pi Presents
    def base_terminate(self):
        self.mon.trace(self, '')
        # set signal to stop the show when all  sub-shows and players have ended
        self.terminate_signal = True
        if self.shower is not None:
            self.shower.terminate()
        elif self.current_player is not None:
            self.ending_reason = 'killed'
            Show.base_close_or_unload(self)
        else:
            self.end('killed',
                     ' terminated with no shower or player to terminate')

# respond to input events

    def base_handle_input_event(self, symbol):
        self.mon.log(
            self, self.show_params['show-ref'] + ' Show Id: ' +
            str(self.show_id) + ": received input event: " + symbol)

        if self.shower is not None:
            self.shower.handle_input_event(symbol)
        else:
            self.handle_input_event_this_show(symbol)

    #dummy must be overridden in derived class
    def handle_input_event_this_show(self, symbol):
        self.mon.err(self, "input_pressed_this_show not overidden")
        self.ending_reason = 'killed'
        Show.base_close_or_unload(self)

    def base_load_show_background(self):
        # load show background image
        if self.background_file != '':
            background_img_file = self.base_complete_path(self.background_file)
            if not os.path.exists(background_img_file):
                return 'error', "Show background file not found " + background_img_file
            else:
                pil_background_img = Image.open(background_img_file)
                # print 'pil_background_img ',pil_background_img
                image_width, image_height = pil_background_img.size
                window_width = self.show_canvas_width
                window_height = self.show_canvas_height
                if image_width != window_width or image_height != window_height:
                    pil_background_img = pil_background_img.resize(
                        (window_width, window_height))
                self.background = ImageTk.PhotoImage(pil_background_img)
                del pil_background_img
                # print 'self.background ',self.background
                self.background_obj = self.canvas.create_image(
                    self.show_canvas_x1,
                    self.show_canvas_y1,
                    image=self.background,
                    anchor=NW)
                self.canvas.itemconfig(self.background_obj, state='hidden')
                self.canvas.update_idletasks()
                # print '\nloaded background_obj: ',self.background_obj
                return 'normal', 'show background loaded'
        else:
            return 'normal', 'no backgound to load'

    def base_show_show_background(self):
        if self.background_obj is not None:
            # print 'show show background'
            self.canvas.itemconfig(self.background_obj, state='normal')
            # self.canvas.update_idletasks( )

    def base_withdraw_show_background(self):
        self.mon.trace(self, '')
        if self.background_obj is not None:
            # print 'withdraw background obj', self.background_obj
            self.canvas.itemconfig(self.background_obj, state='hidden')
            # self.canvas.update_idletasks( )

    def base_delete_show_background(self):
        if self.background_obj is not None:
            # print 'delete background obj'
            self.canvas.delete(self.background_obj)
            self.background = None
            # self.canvas.update_idletasks( )

# ******************************
# write statiscics
# *********************************

    def write_stats(self, command, show_params, next_track):
        # action, this ref, this name, type, ref, name, location
        if next_track['type'] == 'show':
            # get the show from the showlist
            index = self.showlist.index_of_show(next_track['sub-show'])
            if index < 0:
                self.mon.err(
                    self,
                    "Show not found in showlist: " + next_track['sub-show'])
                self.end('error',
                         'show not in showlist: ' + next_track['sub-show'])
            else:
                target = self.showlist.show(index)
                ref = target['show-ref']
                title = target['title']
                track_type = target['type']
        else:
            # its a track
            ref = next_track['track-ref']
            title = next_track['title']
            track_type = next_track['type']
        if next_track['type'] in ('show', 'message'):
            loc = ''
        else:
            loc = next_track['location']
        self.mon.stats(show_params['type'], show_params['show-ref'],
                       show_params['title'], command, track_type, ref, title,
                       loc)

# ******************************
# lookup controls
# *********************************

    def base_lookup_control(self, symbol, controls_list):
        for control in controls_list:
            if symbol == control[0]:
                return control[1]
        # not found so must be a trigger
        return ''

# ******************************
# Eggtimer
# *********************************

    def display_eggtimer(self):
        text = self.show_params['eggtimer-text']
        if text != '':
            self.egg_timer = self.canvas.create_text(
                int(self.show_params['eggtimer-x']) + self.show_canvas_x1,
                int(self.show_params['eggtimer-y']) + self.show_canvas_y1,
                text=text,
                fill=self.show_params['eggtimer-colour'],
                font=self.show_params['eggtimer-font'],
                anchor='nw')

            self.canvas.update_idletasks()

    def delete_eggtimer(self):
        if self.egg_timer is not None:
            self.canvas.delete(self.egg_timer)
            self.egg_timer = None
            self.canvas.update_idletasks()

# ******************************
# Display Admin Messages
# *********************************

    def display_admin_message(self, text):

        self.admin_message = self.canvas.create_text(
            int(self.show_params['admin-x']) + self.show_canvas_x1,
            int(self.show_params['admin-y']) + self.show_canvas_y1,
            text=text,
            fill=self.show_params['admin-colour'],
            font=self.show_params['admin-font'],
            anchor='nw')

        self.canvas.update_idletasks()

    def delete_admin_message(self):
        if self.admin_message is not None:
            self.canvas.delete(self.admin_message)
            self.canvas.update_idletasks()

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

    def base_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:]
        self.mon.log(self, "Track to load is: " + track_file)
        return track_file

    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]
        if not secs.isdigit() or not minutes.isdigit() or not hours.isdigit():
            return 'error', 'bad time: ' + line, 0
        else:
            return 'normal', '', 3600 * long(hours) + 60 * long(
                minutes) + long(secs)
Пример #2
0
class Player(object):

    # common bits of __init__(...)
    def __init__(
        self,
        show_id,
        showlist,
        root,
        canvas,
        show_params,
        track_params,
        pp_dir,
        pp_home,
        pp_profile,
        end_callback,
        command_callback,
    ):

        # create debugging log object
        self.mon = Monitor()

        self.mon.trace(self, "")

        # instantiate arguments
        self.show_id = show_id
        self.showlist = showlist
        self.root = root
        self.canvas = canvas["canvas-obj"]
        self.show_canvas_x1 = canvas["show-canvas-x1"]
        self.show_canvas_y1 = canvas["show-canvas-y1"]
        self.show_canvas_x2 = canvas["show-canvas-x2"]
        self.show_canvas_y2 = canvas["show-canvas-y2"]
        self.show_canvas_width = canvas["show-canvas-width"]
        self.show_canvas_height = canvas["show-canvas-height"]
        self.show_canvas_centre_x = canvas["show-canvas-centre-x"]
        self.show_canvas_centre_y = canvas["show-canvas-centre-y"]
        self.show_params = show_params
        self.track_params = track_params
        self.pp_dir = pp_dir
        self.pp_home = pp_home
        self.pp_profile = pp_profile
        self.end_callback = end_callback
        self.command_callback = command_callback

        # get background image from profile.
        self.background_file = ""
        if self.track_params["background-image"] != "":
            self.background_file = self.track_params["background-image"]

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

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

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

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

        # create an instance of Animate so we can send animation commands
        self.animate = Animate()

        # initialise state and signals
        self.background_obj = None
        self.show_text_obj = None
        self.track_text_obj = None
        self.hint_obj = None
        self.background = None
        self.freeze_at_end_required = "no"  # overriden by videoplayer
        self.tick_timer = None
        self.terminate_signal = False
        self.play_state = ""

    def pre_load(self):
        # Control other shows at beginning
        self.show_control(self.track_params["show-control-begin"])
        pass

    # common bits of show(....)
    def pre_show(self):
        self.mon.trace(self, "")

        # show_x_content moved to just before ready_callback to improve flicker.
        self.show_x_content()

        # and whatecer the plugin has created
        self.pim.show_plugin()

        # ready callback hides and closes players from previous track, also displays show background
        if self.ready_callback is not None:
            self.ready_callback(self.enable_show_background)

        # create animation events
        reason, message = self.animate.animate(self.animate_begin_text, id(self))
        if reason == "error":
            self.mon.err(self, message)
            self.play_state = "show-failed"
            if self.finished_callback is not None:
                self.finished_callback("error", message)
        else:
            # return to start playing the track.
            self.mon.log(self, ">show track received from show Id: " + str(self.show_id))
            return

    # to keep landscape happy
    def ready_callback(self, enable_show_background):
        self.mon.fatal(self, "ready callback not overridden")
        self.end("error", "ready callback not overridden")

    def finished_callback(self, reason, message):
        self.mon.fatal(self, "finished callback not overridden")
        self.end("error", "finished callback not overridden")

    def closed_callback(self, reason, message):
        self.mon.fatal(self, "closed callback not overridden")
        self.end("error", "closed callback not overridden")

    # Control shows so pass the show control commands back to PiPresents via the command callback
    def show_control(self, show_control_text):
        lines = show_control_text.split("\n")
        for line in lines:
            if line.strip() == "":
                continue
            # print 'show control command: ',line
            self.command_callback(line, self.show_params["show-ref"])

    # *****************
    # hide content and end animation, show control etc.
    # called by ready calback and end
    # *****************

    def hide(self):
        self.mon.trace(self, "")
        # abort the timer
        if self.tick_timer is not None:
            self.canvas.after_cancel(self.tick_timer)
            self.tick_timer = None

        self.hide_x_content()

        # stop the plugin
        if self.track_params["plugin"] != "":
            self.pim.stop_plugin()

        # Control concurrent shows at end
        self.show_control(self.track_params["show-control-end"])

        # clear events list for this track
        if self.track_params["animate-clear"] == "yes":
            self.animate.clear_events_list(id(self))

        # create animation events for ending
        reason, message = self.animate.animate(self.animate_end_text, id(self))
        if reason == "error":
            self.play_state = "show-failed"
            if self.finished_callback is not None:
                self.finished_callback("error", message)
        else:
            return

    def terminate(self):
        self.mon.trace(self, "")
        self.terminate_signal = True
        if self.play_state == "showing":
            # call the derived class's stop method
            self.stop()
        else:
            self.end("killed", "terminate with no track or show open")

    # must be overriden by derived class
    def stop(self):
        self.mon.fatal(self, "stop not overidden by derived class")
        self.play_state = "show-failed"
        if self.finished_callback is not None:
            self.finished_callback("error", "stop not overidden by derived class")

    def get_play_state(self):
        return self.play_state

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

    def end(self, reason, message):
        self.mon.trace(self, "")
        # stop the plugin

        if self.terminate_signal is True:
            reason = "killed"
            self.terminate_signal = False
            self.hide()

        self.end_callback(reason, message)
        self = None

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

    def load_plugin(self):
        # load the plugin if required
        if self.track_params["plugin"] != "":
            reason, message, self.track = self.pim.load_plugin(self.track, self.track_params["plugin"])
            return reason, message

    def draw_plugin(self):
        # load the plugin if required
        if self.track_params["plugin"] != "":
            self.pim.draw_plugin()
            return

    def load_x_content(self, enable_menu):
        self.mon.trace(self, "")
        self.background_obj = None
        self.background = None
        self.track_text_obj = None
        self.show_text_obj = None
        self.hint_obj = None
        self.track_obj = None

        # background image
        if self.background_file != "":
            background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(background_img_file):
                return "error", "Track background file not found " + background_img_file
            else:
                pil_background_img = Image.open(background_img_file)
                # print 'pil_background_img ',pil_background_img
                image_width, image_height = pil_background_img.size
                window_width = self.show_canvas_width
                window_height = self.show_canvas_height
                if image_width != window_width or image_height != window_height:
                    pil_background_img = pil_background_img.resize((window_width, window_height))
                self.background = ImageTk.PhotoImage(pil_background_img)
                del pil_background_img
                self.background_obj = self.canvas.create_image(
                    self.show_canvas_x1, self.show_canvas_y1, image=self.background, anchor=NW
                )
                # print '\nloaded background_obj: ',self.background_obj

        # load the track content.  Dummy function below is overridden in players
        status, message = self.load_track_content()
        if status == "error":
            return "error", message

        # load show text if enabled
        if self.show_params["show-text"] != "" and self.track_params["display-show-text"] == "yes":
            self.show_text_obj = self.canvas.create_text(
                int(self.show_params["show-text-x"]) + self.show_canvas_x1,
                int(self.show_params["show-text-y"]) + self.show_canvas_y1,
                anchor=NW,
                text=self.show_params["show-text"],
                fill=self.show_params["show-text-colour"],
                font=self.show_params["show-text-font"],
            )

        # load track text if enabled
        if self.track_params["track-text"] != "":
            self.track_text_obj = self.canvas.create_text(
                int(self.track_params["track-text-x"]) + self.show_canvas_x1,
                int(self.track_params["track-text-y"]) + self.show_canvas_y1,
                anchor=NW,
                text=self.track_params["track-text"],
                fill=self.track_params["track-text-colour"],
                font=self.track_params["track-text-font"],
            )

        # load instructions if enabled
        if enable_menu is True:
            self.hint_obj = self.canvas.create_text(
                int(self.show_params["hint-x"]) + self.show_canvas_x1,
                int(self.show_params["hint-y"]) + self.show_canvas_y1,
                text=self.show_params["hint-text"],
                fill=self.show_params["hint-colour"],
                font=self.show_params["hint-font"],
                anchor=NW,
            )

        self.display_show_canvas_rectangle()

        self.pim.draw_plugin()

        self.canvas.tag_raise("pp-click-area")
        self.canvas.itemconfig(self.background_obj, state="hidden")
        self.canvas.itemconfig(self.show_text_obj, state="hidden")
        self.canvas.itemconfig(self.track_text_obj, state="hidden")
        self.canvas.itemconfig(self.hint_obj, state="hidden")
        self.canvas.update_idletasks()
        return "normal", "x-content loaded"

    # display the rectangle that is the show canvas
    def display_show_canvas_rectangle(self):
        # coords=[self.show_canvas_x1,self.show_canvas_y1,self.show_canvas_x2-1,self.show_canvas_y2-1]
        # self.canvas.create_rectangle(coords,
        #            outline='yellow',
        #          fill='')
        pass

    # dummy functions to manipulate the track content, overidden in some players,
    # message text in messageplayer
    # image in imageplayer
    # menu stuff in menuplayer

    def load_track_content(self):
        return "normal", "player has no track content to load"

    def show_track_content(self):
        pass

    def hide_track_content(self):
        pass

    def show_x_content(self):
        self.mon.trace(self, "")
        # background colour
        if self.background_colour != "":
            self.canvas.config(bg=self.background_colour)
        # print 'showing background_obj: ', self.background_obj
        # reveal background image and text
        self.canvas.itemconfig(self.background_obj, state="normal")
        self.show_track_content()

        self.canvas.itemconfig(self.show_text_obj, state="normal")
        self.canvas.itemconfig(self.track_text_obj, state="normal")
        self.canvas.itemconfig(self.hint_obj, state="normal")
        # self.canvas.update_idletasks( )

        # decide whether the show background should be enabled.
        # print 'DISPLAY SHOW BG',self.track_params['display-show-background'],self.background_obj
        if self.background_obj is None and self.track_params["display-show-background"] == "yes":
            self.enable_show_background = True
        else:
            self.enable_show_background = False
        # print 'ENABLE SB',self.enable_show_background

    def hide_x_content(self):
        self.mon.trace(self, "")
        self.hide_track_content()
        self.canvas.itemconfig(self.background_obj, state="hidden")
        self.canvas.itemconfig(self.show_text_obj, state="hidden")
        self.canvas.itemconfig(self.track_text_obj, state="hidden")
        self.canvas.itemconfig(self.hint_obj, state="hidden")
        # self.canvas.update_idletasks( )

        self.canvas.delete(self.background_obj)
        self.canvas.delete(self.show_text_obj)
        self.canvas.delete(self.track_text_obj)
        self.canvas.delete(self.hint_obj)
        self.background = None
        # self.canvas.update_idletasks( )

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

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

    # produce an absolute path from the relative one in track paramters
    def complete_path(self, track_file):
        #  complete path of the filename of the selected entry
        if track_file[0] == "+":
            track_file = self.pp_home + track_file[1:]
        # self.mon.log(self,"Background image is "+ track_file)
        return track_file

    # get a text string from resources.cfg
    def resource(self, section, item):
        value = self.rr.get(section, item)
        return value  # False if not found
Пример #3
0
class Player(object):

    # common bits of __init__(...)
    def __init__(self,
                 show_id,
                 showlist,
                 root,
                 canvas,
                 show_params,
                 track_params,
                 pp_dir,
                 pp_home,
                 pp_profile,
                 end_callback,
                 command_callback):

        # create debugging log object
        self.mon=Monitor()

        self.mon.trace(self,'')

        # instantiate arguments
        self.show_id=show_id
        self.showlist=showlist
        self.root=root
        self.canvas=canvas['canvas-obj']
        self.show_canvas_x1 = canvas['show-canvas-x1']
        self.show_canvas_y1 = canvas['show-canvas-y1']
        self.show_canvas_x2 = canvas['show-canvas-x2']
        self.show_canvas_y2 = canvas['show-canvas-y2']
        self.show_canvas_width = canvas['show-canvas-width']
        self.show_canvas_height= canvas['show-canvas-height']
        self.show_canvas_centre_x= canvas['show-canvas-centre-x']
        self.show_canvas_centre_y= canvas['show-canvas-centre-y']
        self.show_params=show_params
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile
        self.end_callback=end_callback
        self.command_callback=command_callback
                
        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image'] != '':
            self.background_file= self.track_params['background-image']

            
        # get background colour from profile.
        if self.track_params['background-colour'] != '':
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']
        
        # get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

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

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

        # create an instance of Animate so we can send animation commands
        self.animate = Animate()

        # initialise state and signals
        self.background_obj=None
        self.show_text_obj=None
        self.track_text_obj=None
        self.hint_obj=None
        self.background=None
        self.freeze_at_end_required='no' # overriden by videoplayer
        self.tick_timer=None
        self.terminate_signal=False
        self.play_state=''



    def pre_load(self):

        pass

              
    # common bits of show(....) 
    def pre_show(self):
        self.mon.trace(self,'')

        # show_x_content moved to just before ready_callback to improve flicker.
        self.show_x_content()
  
        #ready callback hides and closes players from previous track, also displays show background
        if self.ready_callback is not None:
            self.ready_callback(self.enable_show_background)


        # Control other shows and do counters and osc at beginning
        self.show_control(self.track_params['show-control-begin'])

        # and show whatever the plugin has created
        self.show_plugin()
        
        # create animation events
        reason,message=self.animate.animate(self.animate_begin_text,id(self))
        if reason  ==  'error':
            self.mon.err(self,message)
            self.play_state='show-failed'
            if self.finished_callback is not None:
                self.finished_callback('error',message)
        else:
            # return to start playing the track.
            self.mon.log(self,">show track received from show Id: "+ str(self.show_id))
            return


    # to keep landscape happy
    def ready_callback(self,enable_show_background):
        self.mon.fatal(self,'ready callback not overridden')
        self.end('error','ready callback not overridden')

    def finished_callback(self,reason,message):
        self.mon.fatal(self,'finished callback not overridden')
        self.end('error','finished callback not overridden')

    def closed_callback(self,reason,message):
        self.mon.fatal(self,'closed callback not overridden')
        self.end('error','closed callback not overridden')


# Control shows so pass the show control commands back to PiPresents via the command callback
    def show_control(self,show_control_text): 
        lines = show_control_text.split('\n')
        for line in lines:
            if line.strip() == "":
                continue
            # print 'show control command: ',line
            self.command_callback(line, source='track',show=self.show_params['show-ref'])



# *****************
# hide content and end animation, show control etc.
# called by ready calback and end
# *****************

    def hide(self):
        self.mon.trace(self,'')
        # abort the timer
        if self.tick_timer is not None:
            self.canvas.after_cancel(self.tick_timer)
            self.tick_timer=None
        
        self.hide_x_content()
        
        # stop the plugin
        self.hide_plugin()

        # Control concurrent shows at end
        self.show_control(self.track_params['show-control-end'])
        
        # clear events list for this track
        if self.track_params['animate-clear'] == 'yes':
            self.animate.clear_events_list(id(self))
                
        # create animation events for ending
        # !!!!! TEMPORARY FIX
        reason,message=self.animate.animate(self.animate_end_text,id(self))
        if reason == 'error':
            self.mon.err(self,message)
            # self.play_state='show-failed'
            # if self.finished_callback is not None:
                # self.finished_callback('error',message)
        else:
            return


    def terminate(self):
        self.mon.trace(self,'')
        self.terminate_signal=True
        if self.play_state == 'showing':
            # call the derived class's stop method
            self.stop()
        else:
            self.end('killed','terminate with no track or show open')

    # must be overriden by derived class
    def stop(self):
        self.mon.fatal(self,'stop not overidden by derived class')
        self.play_state='show-failed'
        if self.finished_callback is not None:
            self.finished_callback('error','stop not overidden by derived class')


    def get_play_state(self):
        return self.play_state
  
# *****************
# ending the player
# *****************

    def end(self,reason,message):
        self.mon.trace(self,'')
        # stop the plugin

        if self.terminate_signal is True:
            reason='killed'
            self.terminate_signal=False
            self.hide()

        self.end_callback(reason,message)
        self=None


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

    def load_plugin(self):
        # called in load before load_x_content modify the track here
        if self.track_params['plugin'] != '':
            reason,message,self.track = self.pim.load_plugin(self.track,self.track_params['plugin'])
            return reason,message
        
    def show_plugin(self):
        # called at show time, write to the track here if you need it after show_control_begin (counters)
        if self.track_params['plugin'] != '':
            self.pim.show_plugin()

    def hide_plugin(self):
        # called at the end of the track
        if self.track_params['plugin'] != '':
            self.pim.stop_plugin()

    def load_x_content(self,enable_menu):
        self.mon.trace(self,'')
        self.background_obj=None
        self.background=None
        self.track_text_obj=None
        self.show_text_obj=None
        self.hint_obj=None
        self.track_obj=None

        
        # background image
        if self.background_file != '':
            background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(background_img_file):
                return 'error',"Track background file not found "+ background_img_file
            else:
                try:
                    pil_background_img=Image.open(background_img_file)
                except:
                    pil_background_img=None
                    self.background=None
                    self.background_obj=None
                    return 'error','Track background, not a recognised image format '+ background_img_file                
                # print 'pil_background_img ',pil_background_img
                image_width,image_height=pil_background_img.size
                window_width=self.show_canvas_width
                window_height=self.show_canvas_height
                if image_width != window_width or image_height != window_height:
                    pil_background_img=pil_background_img.resize((window_width, window_height))
                self.background = ImageTk.PhotoImage(pil_background_img)
                del pil_background_img
                self.background_obj = self.canvas.create_image(self.show_canvas_x1,
                                                               self.show_canvas_y1,
                                                               image=self.background,
                                                               anchor=NW)
                # print '\nloaded background_obj: ',self.background_obj


        # load the track content.  Dummy function below is overridden in players          
        status,message=self.load_track_content()
        if status == 'error':
            return 'error',message
                          
        # load show text if enabled
        if self.show_params['show-text'] !=  '' and self.track_params['display-show-text'] == 'yes':

            x,y,anchor,justify=calculate_text_position(self.show_params['show-text-x'],self.show_params['show-text-y'],
                                     self.show_canvas_x1,self.show_canvas_y1,
                                     self.show_canvas_centre_x,self.show_canvas_centre_y,
                                     self.show_canvas_x2,self.show_canvas_y2,self.show_params['show-text-justify'])
 
            self.show_text_obj=self.canvas.create_text(x,y,
                                                       anchor=anchor,
                                                       justify=justify,
                                                       text=self.show_params['show-text'],
                                                       fill=self.show_params['show-text-colour'],
                                                       font=self.show_params['show-text-font'])


        # load track text if enabled

        if self.track_params['track-text-x'] =='':
            track_text_x= self.show_params['track-text-x']
        else:
            track_text_x= self.track_params['track-text-x']


        if self.track_params['track-text-y'] =='':
            track_text_y= self.show_params['track-text-y']
        else:
            track_text_y= self.track_params['track-text-y']

        if self.track_params['track-text-justify'] =='':
            track_text_justify= self.show_params['track-text-justify']
        else:
            track_text_justify= self.track_params['track-text-justify']

        if self.track_params['track-text-font'] =='':
            track_text_font= self.show_params['track-text-font']
        else:
            track_text_font= self.track_params['track-text-font']
            

        if self.track_params['track-text-colour'] =='':
            track_text_colour= self.show_params['track-text-colour']
        else:
            track_text_colour= self.track_params['track-text-colour']
            
            
        if self.track_params['track-text'] !=  '':

            x,y,anchor,justify=calculate_text_position(track_text_x,track_text_y,
                                     self.show_canvas_x1,self.show_canvas_y1,
                                     self.show_canvas_centre_x,self.show_canvas_centre_y,
                                     self.show_canvas_x2,self.show_canvas_y2,track_text_justify)
 
            
            self.track_text_obj=self.canvas.create_text(x,y,
                                                        anchor=anchor,
                                                        justify=justify,
                                                        text=self.track_params['track-text'],
                                                        fill=track_text_colour,
                                                        font=track_text_font)

        # load instructions if enabled
        if enable_menu is  True:

            x,y,anchor,justify=calculate_text_position(self.show_params['hint-x'],self.show_params['hint-y'],
                                     self.show_canvas_x1,self.show_canvas_y1,
                                     self.show_canvas_centre_x,self.show_canvas_centre_y,
                                     self.show_canvas_x2,self.show_canvas_y2,self.show_params['hint-justify'])
 

            self.hint_obj=self.canvas.create_text(x,y,
                                                  justify=justify,
                                                  text=self.show_params['hint-text'],
                                                  fill=self.show_params['hint-colour'],
                                                  font=self.show_params['hint-font'],
                                                  anchor=anchor)

        self.display_show_canvas_rectangle()

        self.canvas.tag_raise('pp-click-area')
        self.canvas.itemconfig(self.background_obj,state='hidden')
        self.canvas.itemconfig(self.show_text_obj,state='hidden')
        self.canvas.itemconfig(self.track_text_obj,state='hidden')
        self.canvas.itemconfig(self.hint_obj,state='hidden')
        self.canvas.update_idletasks( )
        return 'normal','x-content loaded'


    # display the rectangle that is the show canvas
    def display_show_canvas_rectangle(self):
        # coords=[self.show_canvas_x1,self.show_canvas_y1,self.show_canvas_x2-1,self.show_canvas_y2-1]
        # self.canvas.create_rectangle(coords,
        #            outline='yellow',
        #          fill='')
        pass


    # dummy functions to manipulate the track content, overidden in some players,
    # message text in messageplayer
    # image in imageplayer
    # menu stuff in menuplayer
        
    def load_track_content(self):
        return 'normal','player has no track content to load'

    def show_track_content(self):
        pass

    def hide_track_content(self):
        pass

    def show_x_content(self):
        self.mon.trace(self,'')
        # background colour
        if  self.background_colour != '':
            self.canvas.config(bg=self.background_colour)
        # print 'showing background_obj: ', self.background_obj
        # reveal background image and text
        self.canvas.itemconfig(self.background_obj,state='normal')
        self.show_track_content()

        self.canvas.itemconfig(self.show_text_obj,state='normal')
        self.canvas.itemconfig(self.track_text_obj,state='normal')
        self.canvas.itemconfig(self.hint_obj,state='normal')
        # self.canvas.update_idletasks( )

        # decide whether the show background should be enabled.
        # print 'DISPLAY SHOW BG',self.track_params['display-show-background'],self.background_obj
        if self.background_obj is None and self.track_params['display-show-background']=='yes':
            self.enable_show_background=True
        else:
            self.enable_show_background=False
        # print 'ENABLE SB',self.enable_show_background


    def hide_x_content(self):
        self.mon.trace(self,'')
        self.hide_track_content()
        self.canvas.itemconfig(self.background_obj,state='hidden')
        self.canvas.itemconfig(self.show_text_obj,state='hidden')
        self.canvas.itemconfig(self.track_text_obj,state='hidden')
        self.canvas.itemconfig(self.hint_obj,state='hidden')
        # self.canvas.update_idletasks( )
        
        self.canvas.delete(self.background_obj)
        self.canvas.delete(self.show_text_obj)
        self.canvas.delete(self.track_text_obj)
        self.canvas.delete(self.hint_obj)
        self.background=None
        # self.canvas.update_idletasks( )


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

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

    # produce an absolute path from the relative one in track paramters
    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0] == "+":
            track_file=self.pp_home+track_file[1:]
        elif track_file[0] == "@":
            track_file=self.pp_profile+track_file[1:]
        return track_file
        
    # get a text string from resources.cfg
    def resource(self,section,item):
        value=self.rr.get(section,item)
        return value # False if not found
Пример #4
0
class Player(object):

    # common bits of __init__(...)
    def __init__(self, show_id, showlist, root, canvas, show_params,
                 track_params, pp_dir, pp_home, pp_profile, end_callback,
                 command_callback):

        # create debugging log object
        self.mon = Monitor()

        self.mon.trace(self, '')

        # instantiate arguments
        self.show_id = show_id
        self.showlist = showlist
        self.root = root
        self.canvas = canvas['canvas-obj']
        self.show_canvas_x1 = canvas['show-canvas-x1']
        self.show_canvas_y1 = canvas['show-canvas-y1']
        self.show_canvas_x2 = canvas['show-canvas-x2']
        self.show_canvas_y2 = canvas['show-canvas-y2']
        self.show_canvas_width = canvas['show-canvas-width']
        self.show_canvas_height = canvas['show-canvas-height']
        self.show_canvas_centre_x = canvas['show-canvas-centre-x']
        self.show_canvas_centre_y = canvas['show-canvas-centre-y']
        self.show_params = show_params
        self.track_params = track_params
        self.pp_dir = pp_dir
        self.pp_home = pp_home
        self.pp_profile = pp_profile
        self.end_callback = end_callback
        self.command_callback = command_callback

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

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

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

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

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

        # create an instance of Animate so we can send animation commands
        self.animate = Animate()

        # initialise state and signals
        self.background_obj = None
        self.show_text_obj = None
        self.track_text_obj = None
        self.hint_obj = None
        self.background = None
        self.freeze_at_end_required = 'no'  # overriden by videoplayer
        self.tick_timer = None
        self.terminate_signal = False
        self.play_state = ''

    def pre_load(self):
        # Control other shows at beginning
        self.show_control(self.track_params['show-control-begin'])
        pass

    # common bits of show(....)
    def pre_show(self):
        self.mon.trace(self, '')

        # show_x_content moved to just before ready_callback to improve flicker.
        self.show_x_content()

        # and whatecer the plugin has created
        self.pim.show_plugin()

        #ready callback hides and closes players from previous track, also displays show background
        if self.ready_callback is not None:
            self.ready_callback(self.enable_show_background)

        # create animation events
        reason, message = self.animate.animate(self.animate_begin_text,
                                               id(self))
        if reason == 'error':
            self.mon.err(self, message)
            self.play_state = 'show-failed'
            if self.finished_callback is not None:
                self.finished_callback('error', message)
        else:
            # return to start playing the track.
            self.mon.log(
                self,
                ">show track received from show Id: " + str(self.show_id))
            return

    # to keep landscape happy
    def ready_callback(self, enable_show_background):
        self.mon.fatal(self, 'ready callback not overridden')
        self.end('error', 'ready callback not overridden')

    def finished_callback(self, reason, message):
        self.mon.fatal(self, 'finished callback not overridden')
        self.end('error', 'finished callback not overridden')

    def closed_callback(self, reason, message):
        self.mon.fatal(self, 'closed callback not overridden')
        self.end('error', 'closed callback not overridden')

# Control shows so pass the show control commands back to PiPresents via the command callback

    def show_control(self, show_control_text):
        lines = show_control_text.split('\n')
        for line in lines:
            if line.strip() == "":
                continue
            # print 'show control command: ',line
            self.command_callback(line,
                                  source='track',
                                  show=self.show_params['show-ref'])

# *****************
# hide content and end animation, show control etc.
# called by ready calback and end
# *****************

    def hide(self):
        self.mon.trace(self, '')
        # abort the timer
        if self.tick_timer is not None:
            self.canvas.after_cancel(self.tick_timer)
            self.tick_timer = None

        self.hide_x_content()

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

        # Control concurrent shows at end
        self.show_control(self.track_params['show-control-end'])

        # clear events list for this track
        if self.track_params['animate-clear'] == 'yes':
            self.animate.clear_events_list(id(self))

        # create animation events for ending
        reason, message = self.animate.animate(self.animate_end_text, id(self))
        if reason == 'error':
            self.play_state = 'show-failed'
            if self.finished_callback is not None:
                self.finished_callback('error', message)
        else:
            return

    def terminate(self):
        self.mon.trace(self, '')
        self.terminate_signal = True
        if self.play_state == 'showing':
            # call the derived class's stop method
            self.stop()
        else:
            self.end('killed', 'terminate with no track or show open')

    # must be overriden by derived class
    def stop(self):
        self.mon.fatal(self, 'stop not overidden by derived class')
        self.play_state = 'show-failed'
        if self.finished_callback is not None:
            self.finished_callback('error',
                                   'stop not overidden by derived class')

    def get_play_state(self):
        return self.play_state

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

    def end(self, reason, message):
        self.mon.trace(self, '')
        # stop the plugin

        if self.terminate_signal is True:
            reason = 'killed'
            self.terminate_signal = False
            self.hide()

        self.end_callback(reason, message)
        self = None

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

    def load_plugin(self):
        # load the plugin if required
        if self.track_params['plugin'] != '':
            reason, message, self.track = self.pim.load_plugin(
                self.track, self.track_params['plugin'])
            return reason, message

    def draw_plugin(self):
        # load the plugin if required
        if self.track_params['plugin'] != '':
            self.pim.draw_plugin()
            return

    def load_x_content(self, enable_menu):
        self.mon.trace(self, '')
        self.background_obj = None
        self.background = None
        self.track_text_obj = None
        self.show_text_obj = None
        self.hint_obj = None
        self.track_obj = None

        # background image
        if self.background_file != '':
            background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(background_img_file):
                return 'error', "Track background file not found " + background_img_file
            else:
                pil_background_img = Image.open(background_img_file)
                # print 'pil_background_img ',pil_background_img
                image_width, image_height = pil_background_img.size
                window_width = self.show_canvas_width
                window_height = self.show_canvas_height
                if image_width != window_width or image_height != window_height:
                    pil_background_img = pil_background_img.resize(
                        (window_width, window_height))
                self.background = ImageTk.PhotoImage(pil_background_img)
                del pil_background_img
                self.background_obj = self.canvas.create_image(
                    self.show_canvas_x1,
                    self.show_canvas_y1,
                    image=self.background,
                    anchor=NW)
                # print '\nloaded background_obj: ',self.background_obj

        # load the track content.  Dummy function below is overridden in players
        status, message = self.load_track_content()
        if status == 'error':
            return 'error', message

        # load show text if enabled
        if self.show_params['show-text'] != '' and self.track_params[
                'display-show-text'] == 'yes':

            x, y, anchor, justify = calculate_text_position(
                self.show_params['show-text-x'],
                self.show_params['show-text-y'], self.show_canvas_x1,
                self.show_canvas_y1, self.show_canvas_centre_x,
                self.show_canvas_centre_y, self.show_canvas_x2,
                self.show_canvas_y2, self.show_params['show-text-justify'])

            self.show_text_obj = self.canvas.create_text(
                x,
                y,
                anchor=anchor,
                justify=justify,
                text=self.show_params['show-text'],
                fill=self.show_params['show-text-colour'],
                font=self.show_params['show-text-font'])

        # load track text if enabled
        if self.track_params['track-text'] != '':

            x, y, anchor, justify = calculate_text_position(
                self.track_params['track-text-x'],
                self.track_params['track-text-y'], self.show_canvas_x1,
                self.show_canvas_y1, self.show_canvas_centre_x,
                self.show_canvas_centre_y, self.show_canvas_x2,
                self.show_canvas_y2, self.track_params['track-text-justify'])

            self.track_text_obj = self.canvas.create_text(
                x,
                y,
                anchor=anchor,
                justify=justify,
                text=self.track_params['track-text'],
                fill=self.track_params['track-text-colour'],
                font=self.track_params['track-text-font'])

        # load instructions if enabled
        if enable_menu is True:

            x, y, anchor, justify = calculate_text_position(
                self.show_params['hint-x'], self.show_params['hint-y'],
                self.show_canvas_x1, self.show_canvas_y1,
                self.show_canvas_centre_x, self.show_canvas_centre_y,
                self.show_canvas_x2, self.show_canvas_y2,
                self.show_params['hint-justify'])

            self.hint_obj = self.canvas.create_text(
                x,
                y,
                justify=justify,
                text=self.show_params['hint-text'],
                fill=self.show_params['hint-colour'],
                font=self.show_params['hint-font'],
                anchor=anchor)

        self.display_show_canvas_rectangle()

        self.pim.draw_plugin()

        self.canvas.tag_raise('pp-click-area')
        self.canvas.itemconfig(self.background_obj, state='hidden')
        self.canvas.itemconfig(self.show_text_obj, state='hidden')
        self.canvas.itemconfig(self.track_text_obj, state='hidden')
        self.canvas.itemconfig(self.hint_obj, state='hidden')
        self.canvas.update_idletasks()
        return 'normal', 'x-content loaded'

    # display the rectangle that is the show canvas
    def display_show_canvas_rectangle(self):
        # coords=[self.show_canvas_x1,self.show_canvas_y1,self.show_canvas_x2-1,self.show_canvas_y2-1]
        # self.canvas.create_rectangle(coords,
        #            outline='yellow',
        #          fill='')
        pass

    # dummy functions to manipulate the track content, overidden in some players,
    # message text in messageplayer
    # image in imageplayer
    # menu stuff in menuplayer

    def load_track_content(self):
        return 'normal', 'player has no track content to load'

    def show_track_content(self):
        pass

    def hide_track_content(self):
        pass

    def show_x_content(self):
        self.mon.trace(self, '')
        # background colour
        if self.background_colour != '':
            self.canvas.config(bg=self.background_colour)
        # print 'showing background_obj: ', self.background_obj
        # reveal background image and text
        self.canvas.itemconfig(self.background_obj, state='normal')
        self.show_track_content()

        self.canvas.itemconfig(self.show_text_obj, state='normal')
        self.canvas.itemconfig(self.track_text_obj, state='normal')
        self.canvas.itemconfig(self.hint_obj, state='normal')
        # self.canvas.update_idletasks( )

        # decide whether the show background should be enabled.
        # print 'DISPLAY SHOW BG',self.track_params['display-show-background'],self.background_obj
        if self.background_obj is None and self.track_params[
                'display-show-background'] == 'yes':
            self.enable_show_background = True
        else:
            self.enable_show_background = False
        # print 'ENABLE SB',self.enable_show_background

    def hide_x_content(self):
        self.mon.trace(self, '')
        self.hide_track_content()
        self.canvas.itemconfig(self.background_obj, state='hidden')
        self.canvas.itemconfig(self.show_text_obj, state='hidden')
        self.canvas.itemconfig(self.track_text_obj, state='hidden')
        self.canvas.itemconfig(self.hint_obj, state='hidden')
        # self.canvas.update_idletasks( )

        self.canvas.delete(self.background_obj)
        self.canvas.delete(self.show_text_obj)
        self.canvas.delete(self.track_text_obj)
        self.canvas.delete(self.hint_obj)
        self.background = None
        # self.canvas.update_idletasks( )

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

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

    # produce an absolute path from the relative one in track paramters
    def complete_path(self, track_file):
        #  complete path of the filename of the selected entry
        if track_file[0] == "+":
            track_file = self.pp_home + track_file[1:]
        elif track_file[0] == "@":
            track_file = self.pp_profile + track_file[1:]
        return track_file

    # get a text string from resources.cfg
    def resource(self, section, item):
        value = self.rr.get(section, item)
        return value  # False if not found
Пример #5
0
class Show(object):


    # ******************************
    # init a show
    # ******************************

    def base__init__(self,
                     show_id,
                     show_params,
                     root,
                     canvas,
                     showlist,
                     pp_dir,
                     pp_home,
                     pp_profile,
                     command_callback):

        # instantiate arguments
        self.show_id=show_id
        self.show_params=show_params
        self.root=root
        self.show_canvas=canvas
        self.canvas=canvas['canvas-obj']
        self.show_canvas_x1 = canvas['show-canvas-x1']
        self.show_canvas_y1 = canvas['show-canvas-y1']
        self.show_canvas_x2 = canvas['show-canvas-x2']
        self.show_canvas_y2 = canvas['show-canvas-y2']
        self.show_canvas_width = canvas['show-canvas-width']
        self.show_canvas_height= canvas['show-canvas-height']
        self.show_canvas_centre_x= canvas['show-canvas-centre-x']
        self.show_canvas_centre_y= canvas['show-canvas-centre-y']
        self.showlist=showlist
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile
        self.command_callback=command_callback

        # init things that will then be reinitialised by derived classes
        self.medialist=None

        # set up logging 
        self.mon=Monitor()
        self.mon.set_log_level(16)


        # create and instance of TimeOfDay scheduler so we can add events
        self.tod=TimeOfDay()
        
        # create an  instance of showmanager so we can init child/subshows
        self.show_manager=ShowManager(self.show_id,self.showlist,self.show_params,self.root,self.show_canvas,self.pp_dir,self.pp_profile,self.pp_home)


        # init variables
        self.current_player=None
        self.previous_player=None
        self.shower=None
        self.previous_shower=None
        self.user_stop_signal= False
        self.exit_signal=False
        self.terminate_signal=False
        self.show_timeout_signal=False
        self.egg_timer=None
        self.admin_message=None
        self.ending_reason=''
        self.background_obj=None
        self.background_file=''
        self.level=0
        self.subshow_kickback_signal=False
        self.kickback_for_next_track=False
        
        # get background image from profile.
        # print 'background', self.show_params['background-image']
        if self.show_params['background-image'] != '':
            self.background_file= self.show_params['background-image']

    def base_play(self,end_callback,show_ready_callback, parent_kickback_signal,level,controls_list):

        """ starts the common parts of the show
              end_callback - function to be called when the show exits- callback gets last player of subshow
              show_ready_callback - callback at start to get previous_player
              top is True when the show is top level (run from [start] or by show command from another show)
              direction_command - 'forward' or 'backward' direction to play a subshow
        """
        # instantiate the arguments
        self.end_callback=end_callback
        self.show_ready_callback=show_ready_callback
        self.parent_kickback_signal=parent_kickback_signal
        self.level=level
        # not needed as controls list is not passed down to subshows.
        # self.controls_list=controls_list
        self.mon.trace(self,self.show_params['show-ref'] + ' at level ' + str(self.level))
        self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Starting show")

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

        # read the medialist for the show
        if self.medialist.open_list(self.medialst_file,self.showlist.sissue()) is 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_ready_callback is not None:
            # get the previous player from calling show its stored in current because its going to be shuffled before use
            self.previous_shower, self.current_player=self.show_ready_callback()
            self.mon.trace(self,' - previous shower and player is ' + self.mon.pretty_inst(self.previous_shower)+ ' ' + self.mon.pretty_inst(self.current_player))

        #load the show background
        reason,message=Show.base_load_show_background(self)
        if reason=='error':
            self.mon.err(self,message)
            self.end('error',message)

    # dummy, must be overidden by derived class
    def subshow_ready_callback(self):
        self.mon.err(self,"subshow_ready_callback not overidden")
        # set what to do when closed or unloaded
        self.ending_reason='killed'
        Show.base_close_or_unload(self)


    def base_subshow_ready_callback(self):
        # callback from begining of a subshow, provide previous player to called show
        # used by show_ready_callback of called show
        # in the case of a menushow last track is always the menu
        self.mon.trace(self,' -  sends ' + self.mon.pretty_inst(self.previous_player))
        return self,self.previous_player


    def base_shuffle(self):
        self.previous_player=self.current_player
        self.current_player = None
        self.mon.trace(self,' - LOOP STARTS WITH current is: ' + self.mon.pretty_inst(self.current_player))
        self.mon.trace(self,'       -  previous is: ' + self.mon.pretty_inst(self.previous_player))



    def base_load_track_or_show(self,selected_track,loaded_callback,end_shower_callback,enable_menu):
        track_type=selected_track['type']
        if track_type == "show":
            # get the show from the showlist
            index = self.showlist.index_of_show(selected_track['sub-show'])
            if index <0:
                self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show'])
                self.end('error','show not in showlist')
            else:
                self.showlist.select(index)
                selected_show=self.showlist.selected_show()
                self.shower=self.show_manager.init_subshow(self.show_id,selected_show,self.show_canvas)
                self.mon.trace(self,' - show is: ' + self.mon.pretty_inst(self.shower) + ' ' + selected_show['show-ref'])
                if self.shower is None:
                    self.mon.err(self,"Unknown Show Type: "+ selected_show['type'])
                    self.terminate_signal=True
                    self.what_next_after_showing()
                else:
                    # empty controls list as not used, pleases landscape
                    # print 'send direction to subshow from show',self.kickback_for_next_track
                    # Show.base_withdraw_show_background(self)
                    self.shower.play(end_shower_callback,self.subshow_ready_callback,self.kickback_for_next_track,self.level+1,[])
        else:
            # dispatch track by type
            self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Track type is: "+ track_type)
            
            self.current_player=self.base_init_selected_player(selected_track)
            #menu has no track file
            if selected_track['type']=='menu':
                track_file=''
                
            # messageplayer passes the text not a file name
            elif selected_track['type'] == 'message':
                track_file=selected_track['text']
            else:
                track_file=self.base_complete_path(selected_track['location'])
                
            self.mon.trace(self,' - track is: ' + track_file)
            self.mon.trace(self,' - current_player is: '+ self.mon.pretty_inst(self.current_player))
            self.current_player.load(track_file,
                                     loaded_callback,
                                     enable_menu=enable_menu)

    # DUMMY, must be overidden by derived class
    def what_next_after_showing(self):
        self.mon.err(self,"what_next_after showing not overidden")
        # set what to do when closed or unloaded
        self.ending_reason='killed'
        Show.base_close_or_unload(self)


    def base_init_selected_player(self,selected_track):
        # dispatch track by type
        track_type=selected_track['type']
        self.mon.log(self,"Track type is: "+ track_type)
                                      
        if track_type == "image":
            return ImagePlayer(self.show_id,self.showlist,self.root,self.show_canvas,
                               self.show_params,selected_track,self.pp_dir,self.pp_home,
                               self.pp_profile,self.end,self.command_callback)
    
        elif track_type == "video":
            return VideoPlayer(self.show_id,self.showlist,self.root,self.show_canvas,
                               self.show_params,selected_track,self.pp_dir,self.pp_home,
                               self.pp_profile,self.end,self.command_callback)

        elif track_type == "audio":
            return AudioPlayer(self.show_id,self.showlist,self.root,self.show_canvas,
                               self.show_params,selected_track,self.pp_dir,self.pp_home,
                               self.pp_profile,self.end,self.command_callback)

        elif track_type == "web" and self.show_params['type'] not in ('artmediashow','artliveshow'):
            return BrowserPlayer(self.show_id,self.showlist,self.root,self.show_canvas,
                                 self.show_params,selected_track,self.pp_dir,self.pp_home,
                                 self.pp_profile,self.end,self.command_callback)
  
        elif track_type == "message":
            return MessagePlayer(self.show_id,self.showlist,self.root,self.show_canvas,
                                 self.show_params,selected_track,self.pp_dir,self.pp_home,
                                 self.pp_profile,self.end,self.command_callback)

        elif track_type == "menu":
            return MenuPlayer(self.show_id,self.showlist,self.root,self.show_canvas,
                              self.show_params,selected_track,self.pp_dir,self.pp_home,
                              self.pp_profile,self.end,self.command_callback)
                                   
        else:
            return None


    # DUMMY, must be overidden by derived class
    def track_ready_callback(self,track_background):
        self.mon.err(self,"track_ready_callback not overidden")
        # set what to do when closed or unloaded
        self.ending_reason='killed'
        Show.base_close_or_unload(self)


    # called just before a track is shown to remove the  previous track from the screen
    # and if necessary close it        
    def base_track_ready_callback(self,enable_show_background):
        self.mon.trace(self,'')
        # show the show background done for every track but quick operation
        if enable_show_background is True:
            self.base_show_show_background()
        else:
            self.base_withdraw_show_background()
        # !!!!!!!!! withdraw the background from the parent show
        if self.previous_shower != None:
            self.previous_shower.base_withdraw_show_background()
        # close the player from the previous track
        if self.previous_player is not  None:
            self.mon.trace(self,' - hiding previous: ' + self.mon.pretty_inst(self.previous_player))
            self.previous_player.hide()
            # print 'Not None  - previous state is',self.previous_player.get_play_state()
            if self.previous_player.get_play_state() == 'showing':
                # print 'showing so closing previous'
                # showing or frozen
                self.mon.trace(self,' - closing previous: ' + self.mon.pretty_inst(self.previous_player))
                self.previous_player.close(self._base_closed_callback_previous)
            else:
                self.mon.trace(self,' - previous is none\n')
                self.previous_player=None
        self.canvas.update_idletasks( )


    def _base_closed_callback_previous(self,status,message):
        self.mon.trace(self,' -  previous is None  - was: ' + self.mon.pretty_inst(self.previous_player))
        self.previous_player=None


    # used by end_shower to get the last track of the subshow
    def base_end_shower(self):
        self.mon.trace(self,' -  returned back to level: ' +str(self.level))
        # get the previous subshow and last track it played 
        self.previous_shower,self.current_player=self.shower.base_subshow_ended_callback()
        if self.previous_shower!= None:
            self.subshow_kickback_signal=self.shower.subshow_kickback_signal
            # print 'get subshow kickback from subshow',self.subshow_kickback_signal
            self.previous_shower.base_withdraw_show_background()
            self.base_show_show_background()
        self.previous_player=None
        self.mon.trace(self,'- get previous_player from subshow: ' + self.mon.pretty_inst(self.current_player))
        self.shower=None


    # close or unload the current player when ending the show
    def base_close_or_unload(self):
        self.mon.trace(self,self.mon.pretty_inst(self.current_player))
        # need to test for None because player may be made None by subshow lower down the stack for terminate
        if self.current_player is not None:
            self.mon.trace(self,self.current_player.get_play_state())
            if self.current_player.get_play_state() in ('loaded','showing','show-failed'):
                if self.current_player.get_play_state() == 'loaded':
                    self.mon.trace(self,' - unloading current from: '+self.ending_reason)
                    self.current_player.unload()
                else:
                    self.mon.trace(self,' - closing current from: '  + self.ending_reason)
                    self.current_player.close(None)
            self._wait_for_end()
        else:
            # current_player is None because closed further down show stack
            self.mon.trace(self,' - show ended with current_player=None because: ' + self.ending_reason)

            # if exiting pipresents then need to close previous show else get memotry leak
            # if not exiting pipresents the keep previous so it can be closed when showing the next track
            # print 'CURRENT PLAYER IS NONE' ,self.ending_reason
            if self.ending_reason == 'killed':
                self.base_close_previous()

            elif self.ending_reason == 'error':
                self.base_close_previous()

            elif self.ending_reason == 'exit':
                self.end('normal',"show quit by exit command")

            elif self.ending_reason == 'user-stop':
                self.end('normal',"show quit by stop operation")
                    
            else:
                self.mon.fatal(self,"Unhandled ending_reason: ")
                self.end('error',"Unhandled ending_reason")          


    def _base_closed_callback_current(self,status,message):
        self.mon.trace(self,' current is None  - was: ' + self.mon.pretty_inst(self.current_player))


    # wait for unloading or closing to complete then end
    def _wait_for_end(self):
        self.mon.trace(self, self.mon.pretty_inst(self.current_player))
        if self.current_player is not None:
            self.mon.trace(self,' - play state is ' +self.current_player.get_play_state())
            if self.current_player.play_state not in ('unloaded','closed','load-failed'):  ####
                self.canvas.after(50,self._wait_for_end)
            else:
                self.mon.trace(self,' - current closed '+ self.mon.pretty_inst(self.current_player) + ' ' + self.ending_reason)

                 #why is some of thsi different to close and unload????????????? perhaps because current_player isn't none, just closed
                if self.ending_reason == 'killed':
                    self.current_player.hide()
                    self.current_player=None
                    self.base_close_previous()

                elif self.ending_reason == 'error':
                    self.current_player.hide()
                    self.current_player=None
                    self.base_close_previous()

                elif self.ending_reason == 'exit':
                    self.current_player.hide()
                    self.current_player=None
                    self.base_close_previous()

                elif self.ending_reason == 'change-medialist':
                    self.current_player.hide()
                    self.current_player=None
                    # self.base_close_previous()
                    # go to start of list via wait for trigger.
                    self.wait_for_trigger()
                    
                elif self.ending_reason == 'show-timeout':
                    self.current_player.hide()
                    self.current_player=None
                    self.end('normal',"show timeout")
                    
                elif self.ending_reason == 'user-stop':
                    if self.level !=0:
                        self.end('normal',"show quit by stop operation")
                    else:
                        self.current_player.hide()
                        self.current_player=None
                        self.base_close_previous()
                        
                    
                else:
                    self.mon.fatal(self,"Unhandled ending_reason: " + self.ending_reason)
                    self.end('error',"Unhandled ending_reason")
        else:
            self.mon.trace(self,' - current is None ' +  self.mon.pretty_inst(self.current_player) + ' ' + self.ending_reason)


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

    # dummy, normally overidden by derived class
    def end(self,reason,message):
        self.mon.err(self,"end not overidden")
        self.base_end('error',message)

    def base_end(self,reason,message):
        self.base_withdraw_show_background()
        self.base_delete_show_background()
        self.mon.trace(self,' at level ' + str(self.level) + '\n - Current is ' + self.mon.pretty_inst(self.current_player) + '\n - Previous is ' + self.mon.pretty_inst(self.previous_player) + '\n with reason' + reason + '\n\n')
        self.mon.log(self,self.show_params['show-ref']+ ' Show Id: '+ str(self.show_id)+ ": Ending Show")
        self.end_callback(self.show_id,reason,message)
        self=None


    def base_subshow_ended_callback(self):
        # called by end_shower of a parent show  to get the last track of the subshow and the subshow
        self.mon.trace(self,' -  returns ' + self.mon.pretty_inst(self.current_player))
        return self, self.current_player


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


    def base_close_previous(self):
        self.mon.trace(self,'')
        # close the player from the previous track
        if self.previous_player is not  None:
            self.mon.trace(self,' - previous not None ' + self.mon.pretty_inst(self.previous_player))
            if self.previous_player.get_play_state() == 'showing':
                # showing or frozen
                self.mon.trace(self,' - closing previous ' + self.mon.pretty_inst(self.previous_player))
                self.previous_player.close(self._base_close_previous_callback)
            else:
                self.mon.trace(self,'previous is not showing')
                self.previous_player.hide()
                self.previous_player=None
                self.end(self.ending_reason,'')
        else:
            self.mon.trace(self,' - previous is None')
            self.end(self.ending_reason,'')
            
                

    def _base_close_previous_callback(self,status,message):
        self.mon.trace(self, ' -  previous is None  - was ' + self.mon.pretty_inst(self.previous_player))
        self.previous_player.hide()
        self.previous_player=None
        self.end(self.ending_reason,'')


    # exit received from external source
    def base_exit(self):
        self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Exit received")
        self.mon.trace(self,'')
        # set signal to exit the show when all  sub-shows and players have ended
        self.exit_signal=True
        # then stop subshow or tracks.
        if self.shower is not None:
            self.shower.exit()
        elif self.current_player is not None:
            self.current_player.input_pressed('stop')
        else:
            self.end('normal','exit by ShowManager')

    # show timeout callback received
    def base_show_timeout_stop(self):
        self.mon.trace(self,'')
        # set signal to exit the show when all  sub-shows and players have ended
        self.show_timeout_signal=True
        # then stop and shows or tracks.
        if self.shower is not None:
            self.shower.show_timeout_stop()
        elif self.current_player is not None:
            self.current_player.input_pressed('stop')
        else:
            self.end('normal','stopped by Show Timeout')


    # dummy, normally overidden by derived class
    def terminate(self):
        self.mon.err(self,"terminate not overidden")
        self.base_end('error',"terminate not overidden")

    # terminate Pi Presents
    def base_terminate(self):
        self.mon.trace(self,'')
        # set signal to stop the show when all  sub-shows and players have ended
        self.terminate_signal=True
        if self.shower is not None:
            self.shower.terminate()
        elif self.current_player is not None:
            self.ending_reason='killed'
            Show.base_close_or_unload(self)
        else:
            self.end('killed',' terminated with no shower or player to terminate')


  # respond to input events
    def base_handle_input_event(self,symbol):
        self.mon.log(self, self.show_params['show-ref']+ ' Show Id: '+ str(self.show_id)+": received input event: " + symbol)

        if self.shower is not None:
            self.shower.handle_input_event(symbol)
        else:
            self.handle_input_event_this_show(symbol)

    #dummy must be overridden in derived class
    def handle_input_event_this_show(self,symbol):
        self.mon.err(self,"input_pressed_this_show not overidden")
        self.ending_reason='killed'
        Show.base_close_or_unload(self)
            
    def base_load_show_background(self):
        # load show background image
        if self.background_file != '':
            background_img_file = self.base_complete_path(self.background_file)
            if not os.path.exists(background_img_file):
                return 'error',"Show background file not found "+ background_img_file
            else:
                pil_background_img=Image.open(background_img_file)
                # print 'pil_background_img ',pil_background_img
                image_width,image_height=pil_background_img.size
                window_width=self.show_canvas_width
                window_height=self.show_canvas_height
                if image_width != window_width or image_height != window_height:
                    pil_background_img=pil_background_img.resize((window_width, window_height))
                self.background = ImageTk.PhotoImage(pil_background_img)
                del pil_background_img
                # print 'self.background ',self.background
                self.background_obj = self.canvas.create_image(self.show_canvas_x1,
                                                               self.show_canvas_y1,
                                                               image=self.background,
                                                               anchor=NW)
                self.canvas.itemconfig(self.background_obj,state='hidden')
                self.canvas.update_idletasks( )
                # print '\nloaded background_obj: ',self.background_obj
                return 'normal','show background loaded'
        else:
            return 'normal','no backgound to load'
              
    def base_show_show_background(self):
        if self.background_obj is not None:
            # print 'show show background'
            self.canvas.itemconfig(self.background_obj,state='normal')
            # self.canvas.update_idletasks( )    

    def base_withdraw_show_background(self):
        self.mon.trace(self,'')
        if self.background_obj is not None:
            # print 'withdraw background obj', self.background_obj
            self.canvas.itemconfig(self.background_obj,state='hidden')
            # self.canvas.update_idletasks( )


    def base_delete_show_background(self):
        if self.background_obj is not None:
            # print 'delete background obj'
            self.canvas.delete(self.background_obj)
            self.background=None
            # self.canvas.update_idletasks( )


# ******************************
# write statiscics
# *********************************
    def write_stats(self,command,show_params,next_track):
            # action, this ref, this name, type, ref, name, location
            if next_track['type']=='show':
                # get the show from the showlist
                index = self.showlist.index_of_show(next_track['sub-show'])
                if index <0:
                    self.mon.err(self,"Show not found in showlist: "+ next_track['sub-show'])
                    self.end('error','show not in showlist')
                else:
                    target=self.showlist.show(index)
                    ref=target['show-ref']
                    title=target['title']
                    track_type=target['type']
            else:
                # its a track
                ref= next_track['track-ref']
                title=next_track['title']
                track_type=next_track['type']
            if next_track['type'] in ('show','message'):
                loc = ''
            else:
                loc = next_track['location']                 
            self.mon.stats(show_params['type'],show_params['show-ref'],show_params['title'],command,
                            track_type,ref,title,loc)
            
 


# ******************************
# lookup controls 
# *********************************
    def base_lookup_control(self,symbol,controls_list):
        for control in controls_list:
            if symbol == control[0]:
                return control[1]
        # not found so must be a trigger
        return ''

# ******************************
# Eggtimer
# *********************************
        
    def display_eggtimer(self):
        text=self.show_params['eggtimer-text']
        if text != '':
            self.egg_timer=self.canvas.create_text(int(self.show_params['eggtimer-x'])+ self.show_canvas_x1,
                                                   int(self.show_params['eggtimer-y']) + self.show_canvas_y1,
                                                   text= text,
                                                   fill=self.show_params['eggtimer-colour'],
                                                   font=self.show_params['eggtimer-font'],
                                                   anchor='nw')
            
            self.canvas.update_idletasks( )


    def delete_eggtimer(self):
        if self.egg_timer is not None:
            self.canvas.delete(self.egg_timer)
            self.egg_timer=None
            self.canvas.update_idletasks( )



# ******************************
# Display Admin Messages
# *********************************

    def display_admin_message(self,text):

        self.admin_message=self.canvas.create_text(int(self.show_params['admin-x']) + self.show_canvas_x1,
                                                   int(self.show_params['admin-y'])+self.show_canvas_y1,
                                                   text= text,
                                                   fill=self.show_params['admin-colour'],
                                                   font=self.show_params['admin-font'],
                                                   anchor='nw')
            
        self.canvas.update_idletasks( )


    def delete_admin_message(self):
        if self.admin_message is not None:
            self.canvas.delete(self.admin_message)
            self.canvas.update_idletasks( )


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



    def base_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:]
        self.mon.log(self,"Track to load is: "+ track_file)
        return track_file     
  

    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]
        if not secs.isdigit() or not minutes.isdigit() or not hours.isdigit():
            return 'error','bad time',0
        else:
            return 'normal','',3600*long(hours)+60*long(minutes)+long(secs)