Пример #1
0
class OSCMonitor(object):
    def __init__(self):

        # get command options
        self.command_options = remote_options()

        # get directory holding the code
        self.pp_dir = sys.path[0]

        if not os.path.exists(self.pp_dir + os.sep + "pipresents.py"):
            tkinter.messagebox.showwarning("Pi Presents",
                                           "Bad Application Directory")
            exit()

        # Initialise logging
        Monitor.log_path = self.pp_dir
        self.mon = Monitor()
        self.mon.init()

        Monitor.classes = ['OSCMonitor', 'OSCConfig', 'OSCEditor']

        Monitor.log_level = int(self.command_options['debug'])

        self.mon.log(self, "Pi Presents Monitor is starting")
        self.mon.log(self, " OS and separator " + os.name + '  ' + os.sep)
        self.mon.log(self,
                     "sys.path[0] -  location of code: code " + sys.path[0])

        self.root = Tk()

        # initialise OSC config class
        self.osc_config = OSCConfig()

        # read the options and allow their editing
        self.osc_config_file = self.pp_dir + os.sep + 'pp_config' + os.sep + 'pp_oscmonitor.cfg'
        self.read_create_osc()

        if self.osc_config.slave_enabled != 'yes':
            self.mon.err(self, 'OSC Slave is not enabled in pp_oscmonitor.cfg')
            exit()

        #build gui
        self.setup_gui()

        # initialise
        self.init()

        #and start the system
        self.root.after(1000, self.run_app)
        self.root.mainloop()

    def init(self):
        self.desc.set('Listening for Commands from Master on: ' +
                      self.osc_config.this_unit_ip + ':' +
                      self.osc_config.listen_port)
        self.client = None
        self.server = None
        self.st = None

    def add_status(self, text):
        self.status_display.insert(END, text + '\n')
        self.status_display.see(END)

    def run_app(self):

        if self.osc_config.slave_enabled != 'yes':
            self.mon.err(self, 'Slave not enabled in oscmonitor.cfg')
            return

        if self.osc_config.this_unit_ip == '':
            self.mon.err(self,
                         'IP of own unit must be provided in oscmonitor.cfg')
            return

        if self.osc_config.listen_port == '':
            self.mon.err(self,
                         'Listen port must be provided in oscmonitor.cfg')
            return

        self.client = None
        self.server = None
        self.st = None

        # initialise OSC variables

        self.prefix = '/pipresents'
        self.this_unit = '/' + self.osc_config.this_unit_name
        self.add_status('this unit OSC address is: ' + self.this_unit)
        self.add_status('Listening for Commands from Master on: ' +
                        self.osc_config.this_unit_ip + ':' +
                        self.osc_config.listen_port)

        #connect client for replies then start server to listen for commands
        self.client = OSC.OSCClient()
        self.init_server(self.osc_config.this_unit_ip,
                         self.osc_config.listen_port, self.client)
        self.add_initial_handlers()
        self.start_server()

    # ***************************************
    # OSC CLIENT TO SEND REPLIES
    # ***************************************

    def disconnect_client(self):
        if self.client != None:
            self.client.close()
        return

    # ***************************************
    # OSC SERVER TO LISTEN TO COMMANDS
    # ***************************************

    def init_server(self, ip, port_text, client):
        self.mon.log(self, 'Init Server: ' + ip + ':' + port_text)
        self.server = myOSCServer((ip, int(port_text)), client)

    def start_server(self):
        self.st = threading.Thread(target=self.server.serve_forever)
        self.st.start()

    def close_server(self):
        if self.server != None:
            self.server.close()
        self.mon.log(self, 'Waiting for Server-thread to finish')
        if self.st != None:
            self.st.join()  ##!!!
        self.mon.log(self, 'server thread closed')

    def add_initial_handlers(self):
        pass
        self.server.addMsgHandler('default', self.no_match_handler)
        self.server.addMsgHandler(
            self.prefix + self.this_unit + "/system/server-info",
            self.server_info_handler)
        self.server.addMsgHandler(
            self.prefix + self.this_unit + "/system/loopback",
            self.loopback_handler)

    def no_match_handler(self, addr, tags, stuff, source):
        text = "Message from %s" % OSC.getUrlStr(source) + '\n'
        text += "     %s" % addr + self.pretty_list(stuff)
        self.add_status(text + '\n')

    def server_info_handler(self, addr, tags, stuff, source):
        # send a reply to the client.
        msg = OSC.OSCMessage(self.prefix + '/system/server-info-reply')
        msg.append(self.osc_config.this_unit_name)
        msg.append(self.server.getOSCAddressSpace())

        text = "Message from %s" % OSC.getUrlStr(source) + '\n'
        text += "     %s" % addr + self.pretty_list(stuff)
        self.add_status(text)
        self.add_status('Sent reply to Server Info request to %s:' %
                        OSC.getUrlStr(source) + '\n')
        return msg

    def loopback_handler(self, addr, tags, stuff, source):

        # send a reply to the client.
        msg = OSC.OSCMessage(self.prefix + '/system/loopback-reply')

        text = "Message from %s" % OSC.getUrlStr(source) + '\n'
        text += "     %s" % addr + self.pretty_list(stuff)
        self.add_status(text + '\n' + 'Sent reply to Loopback request to %s:' %
                        OSC.getUrlStr(source) + '\n')
        return msg

    def pretty_list(self, fields):
        text = ' '
        for field in fields:
            text += str(field) + ' '
        return text

    # ***************************************
    # INIT EXIT MISC
    # ***************************************

    def e_edit_osc(self):
        self.disconnect_client()
        self.close_server()
        self.edit_osc()
        self.read_create_osc()
        self.init()
        self.add_status('\n\n\nRESTART')
        self.run_app()

    def app_exit(self):
        self.disconnect_client()
        self.close_server()
        if self.root is not None:
            self.root.destroy()
        self.mon.finish()
        sys.exit()

    def show_help(self):
        tkinter.messagebox.showinfo("Help", "Read 'manual.pdf'")

    def about(self):
        tkinter.messagebox.showinfo(
            "About", "Simple Remote Monitor for Pi Presents\n" +
            "Author: Ken Thompson" +
            "\nWebsite: http://pipresents.wordpress.com/")

    def setup_gui(self):
        # set up the gui

        # root is the Tkinter root widget
        self.root.title("Remote Monitor for Pi Presents")

        # self.root.configure(background='grey')

        self.root.resizable(False, False)

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

        # bind some display fields
        self.desc = StringVar()
        self.filename = StringVar()
        self.display_show = StringVar()
        self.results = StringVar()
        self.status = StringVar()

        # define menu
        menubar = Menu(self.root)

        osc_configmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='Options', menu=osc_configmenu)
        osc_configmenu.add_command(label='Edit', command=self.e_edit_osc)

        helpmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='Help', menu=helpmenu)
        helpmenu.add_command(label='Help', command=self.show_help)
        helpmenu.add_command(label='About', command=self.about)

        self.root.config(menu=menubar)

        # info frame
        info_frame = Frame(self.root, padx=5, pady=5)
        info_frame.pack(side=TOP, fill=BOTH, expand=1)
        info_name = Label(info_frame,
                          text="Slave Unit's Name: " +
                          self.osc_config.this_unit_name,
                          font="arial 12 bold")
        info_name.pack(side=TOP)
        info_this_address = Label(info_frame,
                                  textvariable=self.desc,
                                  font="arial 12 bold")
        info_this_address.pack(side=TOP)

        # status_frame
        status_frame = Frame(self.root, padx=5, pady=5)
        status_frame.pack(side=TOP, fill=BOTH, expand=1)
        status_label = Label(status_frame,
                             text="Status:",
                             font="arial 12 bold")
        status_label.pack(side=LEFT)
        scrollbar = Scrollbar(status_frame, orient=VERTICAL)
        self.status_display = Text(status_frame,
                                   height=20,
                                   yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.status_display.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.status_display.pack(side=LEFT, fill=BOTH, expand=1)


# ***************************************
#  OSC CONFIGURATION
# ***************************************

    def read_create_osc(self):
        if self.osc_config.read(self.osc_config_file) is False:
            self.osc_config.create(self.osc_config_file, 'slave')
            eosc = OSCEditor(self.root, self.osc_config_file, 'slave',
                             'Create OSC Monitor Configuration')
            self.osc_config.read(self.osc_config_file)

    def edit_osc(self):
        if self.osc_config.read(self.osc_config_file) is False:
            self.osc_config.create(self.osc_config_file)
        eosc = OSCEditor(self.root, self.osc_config_file, 'slave',
                         'Edit OSC Monitor Configuration')
Пример #2
0
class OSCDriver(object):

    # executed by main program
    def  init(self,pp_profile,show_command_callback,input_event_callback,output_event_callback):

        self.pp_profile=pp_profile
        self.show_command_callback=show_command_callback
        self.input_event_callback=input_event_callback
        self.output_event_callback=output_event_callback

        self.mon=Monitor()
        config_file=self.pp_profile + os.sep +'pp_io_config'+os.sep+ 'osc.cfg'
        if not os.path.exists(config_file):
            self.mon.err(self, 'OSC Configuration file nof found: '+config_file)
            return'error','OSC Configuration file nof found: '+config_file
        
        self.mon.log(self, 'OSC Configuration file found at: '+config_file)        
        self.options=OSCConfig()
        # only reads the  data for required unit_type 
        if self.options.read(config_file) ==False:
            return 'error','failed to read osc.cfg'

        self.prefix='/pipresents'
        self.this_unit='/' + self.options.this_unit_name
        self.this_unit_type = self.options.this_unit_type

        self.reply_client=None
        self.command_client=None
        self.client=None
        self.server=None

        if self.this_unit_type not in ('master','slave','master+slave'):
            return 'error','this unit type not known: '+self.this_unit_type

        if self.this_unit_type in('slave','master+slave'):
            #start the client that sends replies to controlling unit
            self.reply_client=OSC.OSCClient()
            self.mon.log(self, 'sending replies to controller at: '+self.options.controlled_by_ip+':'+self.options.controlled_by_port)
            self.reply_client.connect((self.options.controlled_by_ip,int(self.options.controlled_by_port)))
            self.mon.log(self,'sending repiles to: '+ str(self.reply_client))
            self.client=self.reply_client
            
        if self.this_unit_type in ('master','master+slave'):
            #start the client that sends commands to the controlled unit
            self.command_client=OSC.OSCClient()
            self.command_client.connect((self.options.controlled_unit_1_ip,int(self.options.controlled_unit_1_port)))
            self.mon.log(self, 'sending commands to controled unit at: '+self.options.controlled_unit_1_ip+':'+self.options.controlled_unit_1_port)
            self.mon.log(self,'sending commands to: '+str(self.command_client))
            self.client=self.command_client

        #start the listener's server
        self.mon.log(self, 'listen to commands from controlled by unit and replies from controlled units on: ' + self.options.this_unit_ip+':'+self.options.this_unit_port)
        self.server=myOSCServer((self.options.this_unit_ip,int(self.options.this_unit_port)),self.client)
        # return_port=int(self.options.controlled_by_port)
        self.mon.log(self,'listening on: '+str(self.server))
        self.add_initial_handlers()
        return 'normal','osc.cfg read'

    def terminate(self):
        if self.server != None:
            self.server.close()
        self.mon.log(self, 'Waiting for Server-thread to finish')
        if self.st != None:
            self.st.join() ##!!!
        self.mon.log(self,'server thread closed')
        self.client.close()



    def start_server(self):
        # Start OSCServer
        self.mon.log(self,'Starting OSCServer')
        self.st = threading.Thread( target = self.server.serve_forever )
        self.st.start()


    def add_initial_handlers(self):
        self.server.addMsgHandler('default', self.no_match_handler)        
        self.server.addMsgHandler(self.prefix+self.this_unit+"/system/server-info", self.server_info_handler)
        self.server.addMsgHandler(self.prefix+self.this_unit+"/system/loopback", self.loopback_handler)
        self.server.addMsgHandler(self.prefix+ self.this_unit+'/core/open', self.open_show_handler)
        self.server.addMsgHandler(self.prefix+ self.this_unit+'/core/close', self.close_show_handler)
        self.server.addMsgHandler(self.prefix+ self.this_unit+'/core/exitpipresents', self.exitpipresents_handler)
        self.server.addMsgHandler(self.prefix+ self.this_unit+'/core/shutdownnow', self.shutdownnow_handler)
        self.server.addMsgHandler(self.prefix+ self.this_unit+'/core/event', self.input_event_handler)
        self.server.addMsgHandler(self.prefix+ self.this_unit+'/core/output', self.output_event_handler)


    def no_match_handler(self,addr, tags, stuff, source):
        self.mon.warn(self,"no match for osc msg with addr : %s" % addr)
        return None


    def server_info_handler(self,addr, tags, stuff, source):
        msg = OSC.OSCMessage(self.prefix+'/'+self.options.controlled_by_name+'/system/server-info-reply')
        msg.append('Unit: '+ self.options.this_unit_name)
        return msg


    def loopback_handler(self,addr, tags, stuff, source):
         # send a reply to the client.
        msg = OSC.OSCMessage(self.prefix+'/'+self.options.controlled_by_name+'/system/loopback-reply')
        return msg

    
    def open_show_handler(self,address, tags, args, source):
        self.prepare_show_command_callback('open ',args,1)
        
    def close_show_handler(self,address, tags, args, source):
        self.prepare_show_command_callback('close ', args,1)

    def exitpipresents_handler(self,address, tags, args, source):
        self.prepare_show_command_callback('exitpipresents',args,0)

    def shutdownnow_handler(self,address, tags, args, source):
        self.prepare_show_command_callback('shutdownnow',args,0)

    def prepare_show_command_callback(self,command,args,limit):
        if len(args) == limit:
            if limit !=0:
                self.show_command_callback(command+args[0])
            else:
                self.show_command_callback(command)                
        else:
            self.mon.warn(self,'OSC show command does not have '+limit +' argument - ignoring')  

    def input_event_handler(self,address, tags, args, source):
        if len(args) == 1:
            self.input_event_callback(args[0],'OSC')
        else:
            self.mon.warn(self,'OSC input event does not have 1 argument - ignoring')    


    def output_event_handler(self,address, tags, args, source):
        if len(args) !=0:
            # delay symbol,param_type,param_values,req_time as a string
            text='0 '
            for arg in args:
                text= text+ arg + ' '
            text = text + '0'
            self.output_event_callback(text)
        else:
            self.mon.warn(self,'OSC output event has no arguments - ignoring')      


    #send messages to controlled units
    # parses the message string into fields and sends - NO error checking
    def send_command(self,text):
        self.mon.log(self,'send OSC Command: ' + text )
        if self.this_unit_type not in ('master','remote','master+slave'):
            self.mon.warn(self,'Unit is not an OSC Master, ignoring command')
            return
        fields=text.split()
        address = fields[0]
        # print 'ADDRESS'+address
        address_fields=address[1:].split('/')
        if address_fields[0] != 'pipresents':
            self.mon.warn(self,'prefix is not pipresents: '+address_fields[0])
        if address_fields[1] != self.options.controlled_unit_1_name:
             self.mon.warn(self,'not sending OSC to the controlled unit: ' +self.options.controlled_unit_1_name + ' is '+ address_fields[1])
        arg_list=fields[1:]
        self.send(address,arg_list)


    def send(self,address,arg_list):
        # print self.command_client
        msg = OSC.OSCMessage()
        # print address
        msg.setAddress(address)
        for arg in arg_list:
            # print arg
            msg.append(arg)
        self.command_client.send(msg)    
Пример #3
0
class OSCRemote(object):
    def __init__(self):

        self.editor_issue = "1.3"

        # get command options
        self.command_options = remote_options()

        # get directory holding the code
        self.pp_dir = sys.path[0]

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

        # Initialise logging
        Monitor.log_path = self.pp_dir
        self.mon = Monitor()
        self.mon.init()

        Monitor.classes = ['OSCRemote', 'OSCConfig', 'OSCEditor']

        Monitor.log_level = int(self.command_options['debug'])

        self.mon.log(self, "Pi Presents Remote is starting")
        self.mon.log(self, " OS and separator " + os.name + '  ' + os.sep)
        self.mon.log(self,
                     "sys.path[0] -  location of code: code " + sys.path[0])

        self.setup_gui()

        # OSC config class
        self.osc_config = OSCConfig()

        self.init()

        #and start the system
        self.root.after(1000, self.run_app)
        self.root.mainloop()

    def init(self):
        self.osc_config_file = self.pp_dir + os.sep + 'pp_config' + os.sep + 'pp_oscremote.cfg'
        self.read_create_osc()
        self.pp_home_dir = self.osc_config.pp_home_dir
        self.pp_profiles_offset = self.osc_config.pp_profiles_offset
        self.mon.log(self, "Data Home from options is " + self.pp_home_dir)
        self.mon.log(
            self, "Current Profiles Offset from options is " +
            self.pp_profiles_offset)
        self.pp_profile_dir = ''

        self.current_showlist = None
        self.current_show = None
        self.current_show_ref = ''
        self.shows_display.delete(0, END)
        self.results.set('')

    def add_status(self, text):
        self.status_display.insert(END, text + '\n')
        self.status_display.see(END)

    def run_app(self):
        self.client = None
        self.server = None
        self.st = None

        # initialise OSC variables
        self.prefix = '/pipresents'
        self.this_unit = '/' + self.osc_config.this_unit_name
        self.add_status('this unit is: ' + self.this_unit)
        self.controlled_unit = '/' + self.osc_config.controlled_unit_1_name
        self.add_status('controlled unit is: ' + self.controlled_unit)

        #connect client then start server to listen for replies
        self.init_client()
        self.add_status('connecting to controlled unit: ' +
                        self.osc_config.controlled_unit_1_ip + ':' +
                        self.osc_config.controlled_unit_1_port + ' ' +
                        self.osc_config.controlled_unit_1_name)
        self.connect_client(self.osc_config.controlled_unit_1_ip,
                            self.osc_config.controlled_unit_1_port)
        self.add_status('listening for replies on:' +
                        self.osc_config.this_unit_ip + ':' +
                        self.osc_config.this_unit_port)
        self.init_server(self.osc_config.this_unit_ip,
                         self.osc_config.this_unit_port, self.client)
        self.add_initial_handlers()
        self.start_server()

# ***************************************
#  RESPOND TO BUTTONS
# ***************************************

    def open_show(self):
        self.msg_path = '/core/open '
        self.msg_arg_text = self.current_show_ref
        self.display_msg_text()

    def close_show(self):
        self.msg_path = '/core/close '
        self.msg_arg_text = self.current_show_ref
        self.display_msg_text()

    def exit_pipresents(self):
        self.msg_path = '/core/exitpipresents'
        self.msg_arg_text = ''
        self.display_msg_text()

    def play_event(self):
        self.msg_path = '/core/event'
        self.msg_arg_text = 'pp-play'
        self.display_msg_text()

    def pause_event(self):
        self.msg_path = '/core/event'
        self.msg_arg_text = 'pp-pause'
        self.display_msg_text()
        pass

    def stop_event(self):
        self.msg_path = '/core/event'
        self.msg_arg_text = 'pp-stop'
        self.display_msg_text()
        pass

    def up_event(self):
        self.msg_path = '/core/event'
        self.msg_arg_text = 'pp-up'
        self.display_msg_text()

    def down_event(self):
        self.msg_path = '/core/event'
        self.msg_arg_text = 'pp-down'
        self.display_msg_text()

    def output(self):
        self.msg_path = '/core/output'
        self.msg_arg_text = ''
        self.display_msg_text()

    def loopback(self):
        self.msg_path = '/system/loopback'
        self.msg_arg_text = ''
        self.display_msg_text()

    def server_info(self):
        self.msg_path = '/system/server-info'
        self.msg_arg_text = ''
        self.display_msg_text()

    # and put the created text in the results box in the gui
    def display_msg_text(self):
        self.results.set(self.prefix + self.controlled_unit + self.msg_path +
                         ' ' + self.msg_arg_text)

    #calback from the Send button
    # parses the message string into fields and sends - NO error checking
    def send_message(self):
        msg_text = self.results.get()
        self.add_status('Send message:' + msg_text)
        self.mon.log(self, 'send message: ' + msg_text)
        fields = msg_text.split()
        address = fields[0]
        arg_list = fields[1:]
        self.send(address, arg_list)

    # ***************************************
    # OSC CLIENT TO SEND MESSAGES
    # ***************************************

    def init_client(self):
        self.client = OSC.OSCClient()

    def connect_client(self, ip, port):
        self.mon.log(self, 'connect to: ' + ip + ':' + str(port))
        self.client.connect((ip, int(port)))

    def send(self, address, arg_list):
        msg = OSC.OSCMessage()
        msg.setAddress(address)
        for arg in arg_list:
            msg.append(arg)
        self.client.send(msg)

    def disconnect_client(self):
        self.client.close()
        return

    # ***************************************
    # OSC SERVER TO LISTEN TO REPLIES
    # ***************************************

    def init_server(self, ip, port_text, client):
        self.mon.log(self, 'Start Server: ' + ip + ':' + port_text)
        self.server = OSC.OSCServer((ip, int(port_text)), client)

    def start_server(self):
        self.st = threading.Thread(target=self.server.serve_forever)
        self.st.start()

    def close_server(self):
        if self.server != None:
            self.server.close()
        self.mon.log(self, 'Waiting for Server-thread to finish')
        if self.st != None:
            self.st.join()  ##!!!
        self.mon.log(self, 'server thread closed')

    def add_initial_handlers(self):
        self.server.addMsgHandler('default', self.no_match_handler)
        self.server.addMsgHandler(
            self.prefix + self.this_unit + "/system/loopback-reply",
            self.loopback_reply_handler)
        self.server.addMsgHandler(
            self.prefix + self.this_unit + "/system/server-info-reply",
            self.server_info_reply_handler)

    def no_match_handler(self, addr, tags, stuff, source):
        text = ''
        text += "no match for new osc msg from %s" % OSC.getUrlStr(
            source) + '\n'
        text += "with addr : %s" % addr + '\n'
        text += "typetags %s" % tags + '\n'
        text += "data %s" % stuff + '\n'
        self.add_status(text + '\n')

    def loopback_reply_handler(self, addr, tags, stuff, source):
        self.add_status('Loopback reply  received from: ' +
                        self.pretty_list(source))

    def server_info_reply_handler(self, addr, tags, stuff, source):
        self.add_status('Server Information from: ' +
                        self.pretty_list(source) + '\n   ' +
                        self.pretty_list(stuff))

    def pretty_list(self, fields):
        text = ' '
        for field in fields:
            text += str(field) + ' '
        return text

    # ***************************************
    # INIT EXIT MISC
    # ***************************************

    def e_edit_osc(self):
        self.disconnect_client()
        self.close_server()
        self.edit_osc()
        self.init()
        self.add_status('\n\n\nRESTART')
        self.run_app()

    def app_exit(self):
        self.disconnect_client()
        self.close_server()
        if self.root is not None:
            self.root.destroy()
        self.mon.finish()
        sys.exit()

    def show_help(self):
        tkMessageBox.showinfo("Help", "Read 'manual.pdf'")

    def about(self):
        tkMessageBox.showinfo(
            "About", "Simple Remote Control for Pi Presents\n" +
            "Author: Ken Thompson" +
            "\nWebsite: http://pipresents.wordpress.com/")

    def setup_gui(self):
        # set up the gui

        # root is the Tkinter root widget
        self.root = Tk()
        self.root.title("Remote Control for Pi Presents")

        # self.root.configure(background='grey')

        self.root.resizable(False, False)

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

        # bind some display fields
        self.filename = StringVar()
        self.display_show = StringVar()
        self.results = StringVar()
        self.status = StringVar()

        # define menu
        menubar = Menu(self.root)

        profilemenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        profilemenu.add_command(label='Select',
                                command=self.open_existing_profile)
        menubar.add_cascade(label='Profile', menu=profilemenu)

        toolsmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='Tools', menu=toolsmenu)

        osc_configmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='OSC', menu=osc_configmenu)
        osc_configmenu.add_command(label='Edit', command=self.e_edit_osc)

        helpmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='Help', menu=helpmenu)
        helpmenu.add_command(label='Help', command=self.show_help)
        helpmenu.add_command(label='About', command=self.about)

        self.root.config(menu=menubar)

        #top frame
        top_frame = Frame(self.root, padx=5, pady=5)
        top_frame.pack(side=TOP)

        results_label = Label(top_frame,
                              text="Message to Send",
                              font="arial 12 bold")
        results_label.pack(side=LEFT)
        results_display = Entry(top_frame, textvariable=self.results, width=70)
        results_display.pack(side=LEFT, fill=BOTH, expand=1)
        send_button = Button(top_frame,
                             width=5,
                             height=1,
                             text='Send',
                             fg='black',
                             command=self.send_message,
                             bg="light grey")
        send_button.pack(side=RIGHT)

        #bottom frame
        bottom_frame = Frame(self.root, padx=5, pady=5)
        bottom_frame.pack(side=TOP, fill=BOTH, expand=1)
        left_frame = Frame(bottom_frame, padx=5)
        left_frame.pack(side=LEFT)
        right_frame = Frame(bottom_frame, padx=5, pady=5)
        right_frame.pack(side=LEFT)

        suplabel_frame = Frame(right_frame, pady=5)
        suplabel_frame.pack(side=TOP)
        commands_label = Label(suplabel_frame,
                               text="Show Control",
                               font="arial 12 bold")
        commands_label.pack()

        supervisor_frame = Frame(right_frame, pady=5)
        supervisor_frame.pack(side=TOP)

        # supervisor buttons
        add_button = Button(supervisor_frame,
                            width=5,
                            height=1,
                            text='Open\nShow',
                            fg='black',
                            command=self.open_show,
                            bg="light grey")
        add_button.pack(side=LEFT)
        add_button = Button(supervisor_frame,
                            width=5,
                            height=1,
                            text='Close\nShow',
                            fg='black',
                            command=self.close_show,
                            bg="light grey")
        add_button.pack(side=LEFT)
        add_button = Button(supervisor_frame,
                            width=10,
                            height=1,
                            text='Exit\nPi Presents',
                            fg='black',
                            command=self.exit_pipresents,
                            bg="light grey")
        add_button.pack(side=LEFT)

        # events buttons
        oplabel_frame = Frame(right_frame, pady=5)
        oplabel_frame.pack(side=TOP)
        operations_label = Label(oplabel_frame,
                                 text="Input Events",
                                 font="arial 12 bold")
        operations_label.pack()

        operations_frame = Frame(right_frame, pady=5)
        operations_frame.pack(side=TOP)

        add_button = Button(operations_frame,
                            width=5,
                            height=1,
                            text='Play',
                            fg='black',
                            command=self.play_event,
                            bg="light grey")
        add_button.pack(side=LEFT)
        add_button = Button(operations_frame,
                            width=5,
                            height=1,
                            text='Pause',
                            fg='black',
                            command=self.pause_event,
                            bg="light grey")
        add_button.pack(side=LEFT)
        add_button = Button(operations_frame,
                            width=5,
                            height=1,
                            text='Stop',
                            fg='black',
                            command=self.stop_event,
                            bg="light grey")
        add_button.pack(side=LEFT)
        add_button = Button(operations_frame,
                            width=5,
                            height=1,
                            text='Up',
                            fg='black',
                            command=self.up_event,
                            bg="light grey")
        add_button.pack(side=LEFT)
        add_button = Button(operations_frame,
                            width=5,
                            height=1,
                            text='Down',
                            fg='black',
                            command=self.down_event,
                            bg="light grey")
        add_button.pack(side=LEFT)

        # animate buttons
        animate_frame = Frame(right_frame, pady=5)
        animate_frame.pack(side=TOP)
        animate_label = Label(animate_frame,
                              text="Control Outputs",
                              font="arial 12 bold")
        animate_label.pack()

        animate_frame = Frame(right_frame, pady=5)
        animate_frame.pack(side=TOP)

        add_button = Button(animate_frame,
                            width=5,
                            height=1,
                            text='Output',
                            fg='black',
                            command=self.output,
                            bg="light grey")
        add_button.pack(side=LEFT)

        # system buttons
        systemlabel_frame = Frame(right_frame, pady=5)
        systemlabel_frame.pack(side=TOP)
        system_label = Label(systemlabel_frame,
                             text="System",
                             font="arial 12 bold")
        system_label.pack()

        system_frame = Frame(right_frame, pady=5)
        system_frame.pack(side=TOP)

        add_button = Button(system_frame,
                            width=5,
                            height=1,
                            text='Loopback',
                            fg='black',
                            command=self.loopback,
                            bg="light grey")
        add_button.pack(side=LEFT)
        add_button = Button(system_frame,
                            width=10,
                            height=1,
                            text='Server Info',
                            fg='black',
                            command=self.server_info,
                            bg="light grey")
        add_button.pack(side=LEFT)

        # define display of showlist
        shows_title_frame = Frame(left_frame)
        shows_title_frame.pack(side=TOP)
        shows_label = Label(shows_title_frame, text="Shows")
        shows_label.pack()
        shows_frame = Frame(left_frame)
        shows_frame.pack(side=TOP)
        scrollbar = Scrollbar(shows_frame, orient=VERTICAL)
        self.shows_display = Listbox(shows_frame,
                                     selectmode=SINGLE,
                                     height=12,
                                     width=40,
                                     bg="white",
                                     activestyle=NONE,
                                     fg="black",
                                     yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.shows_display.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.shows_display.pack(side=LEFT, fill=BOTH, expand=1)
        self.shows_display.bind("<ButtonRelease-1>", self.e_select_show)

        # status_frame
        status_frame = Frame(self.root, padx=5, pady=5)
        status_frame.pack(side=TOP, fill=BOTH, expand=1)
        status_label = Label(status_frame, text="Status", font="arial 12 bold")
        status_label.pack(side=LEFT)
        scrollbar = Scrollbar(status_frame, orient=VERTICAL)
        self.status_display = Text(status_frame,
                                   height=10,
                                   yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.status_display.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.status_display.pack(side=LEFT, fill=BOTH, expand=1)

    # ***************************************
    # SHOWLIST
    # ***************************************

    def open_existing_profile(self):
        initial_dir = self.pp_home_dir + os.sep + "pp_profiles" + self.pp_profiles_offset
        if os.path.exists(initial_dir) is False:
            self.mon.err(
                self, "Profiles directory not found: " + initial_dir +
                "\n\nHint: Data Home option must end in pp_home")
            return
        dir_path = tkFileDialog.askdirectory(initialdir=initial_dir)
        # dir_path="C:\Users\Ken\pp_home\pp_profiles\\ttt"
        if len(dir_path) > 0:
            self.open_profile(dir_path)

    def open_profile(self, dir_path):
        showlist_file = dir_path + os.sep + "pp_showlist.json"
        if os.path.exists(showlist_file) is False:
            self.mon.err(
                self, "Not a Profile: " + dir_path +
                "\n\nHint: Have you opened the profile directory?")
            return
        self.pp_profile_dir = dir_path
        self.root.title("Remote for Pi Presents - " + self.pp_profile_dir)
        self.open_showlist(self.pp_profile_dir)

    def open_showlist(self, profile_dir):
        showlist_file = profile_dir + os.sep + "pp_showlist.json"
        if os.path.exists(showlist_file) is False:
            self.mon.err(
                self, "showlist file not found at " + profile_dir +
                "\n\nHint: Have you opened the profile directory?")
            self.app_exit()
        self.current_showlist = ShowList()
        self.current_showlist.open_json(showlist_file)
        if float(self.current_showlist.sissue()) != float(self.editor_issue):
            self.mon.err(
                self, "Version of profile does not match Remote: " +
                self.editor_issue)
            self.app_exit()
        self.refresh_shows_display()

    def refresh_shows_display(self):
        self.shows_display.delete(0, self.shows_display.size())
        for index in range(self.current_showlist.length()):
            self.shows_display.insert(
                END,
                self.current_showlist.show(index)['title'] + "   [" +
                self.current_showlist.show(index)['show-ref'] + "]")
        if self.current_showlist.show_is_selected():
            self.shows_display.itemconfig(
                self.current_showlist.selected_show_index(), fg='red')
            self.shows_display.see(self.current_showlist.selected_show_index())

    def e_select_show(self, event):
        print 'select show', self.current_showlist.length()
        if self.current_showlist is not None and self.current_showlist.length(
        ) > 0:
            mouse_item_index = int(event.widget.curselection()[0])
            self.current_showlist.select(mouse_item_index)
            self.current_show_ref = self.current_showlist.selected_show(
            )['show-ref']
            self.refresh_shows_display()
        else:
            self.current_show_ref = ''


# ***************************************
#  OSC CONFIGURATION
# ***************************************

    def read_create_osc(self):
        if self.osc_config.read(self.osc_config_file) is False:
            self.osc_config.create(self.osc_config_file)
            eosc = OSCEditor(self.root, self.osc_config_file, 'remote',
                             'Create OSC Remote Configuration')
            self.osc_config.read(self.osc_config_file)

    def edit_osc(self):
        if self.osc_config.read(self.osc_config_file) is False:
            self.osc_config.create(self.osc_config_file)
        eosc = OSCEditor(self.root, self.osc_config_file, 'remote',
                         'Edit OSC Reomote Configuration')
class OSCDriver(object):

    # executed by main program
    def init(self, pp_profile, manager_unit, preferred_interface, my_ip,
             show_command_callback, input_event_callback, animate_callback):

        self.pp_profile = pp_profile
        self.show_command_callback = show_command_callback
        self.input_event_callback = input_event_callback
        self.animate_callback = animate_callback

        self.mon = Monitor()
        config_file = self.pp_profile + os.sep + 'pp_io_config' + os.sep + 'osc.cfg'
        if not os.path.exists(config_file):
            self.mon.err(self,
                         'OSC Configuration file not found: ' + config_file)
            return 'error', 'OSC Configuration file nof found: ' + config_file

        self.mon.log(self, 'OSC Configuration file found at: ' + config_file)
        self.osc_config = OSCConfig()

        # reads config data
        if self.osc_config.read(config_file) == False:
            return 'error', 'failed to read osc.cfg'

        # unpack config data and initialise

        if self.osc_config.this_unit_name == '':
            return 'error', 'OSC Config -  This Unit has no name'
        if len(self.osc_config.this_unit_name.split()) > 1:
            return 'error', 'OSC config - This Unit Name not a single word: ' + self.osc_config.this_unit_name
        self.this_unit_name = self.osc_config.this_unit_name

        if self.osc_config.this_unit_ip == '':
            self.this_unit_ip = my_ip
        else:
            self.this_unit_ip = self.osc_config.this_unit_ip

        if self.osc_config.slave_enabled == 'yes':
            if not self.osc_config.listen_port.isdigit():
                return 'error', 'OSC Config - Listen port is not a positve number: ' + self.osc_config.listen_port
            self.listen_port = self.osc_config.listen_port

        if self.osc_config.master_enabled == 'yes':
            if not self.osc_config.reply_listen_port.isdigit():
                return 'error', 'OSC Config - Reply Listen port is not a positve number: ' + self.osc_config.reply_listen_port
            self.reply_listen_port = self.osc_config.reply_listen_port

            # prepare the list of slaves
            status, message = self.parse_slaves()
            if status == 'error':
                return status, message

        self.prefix = '/pipresents'
        self.this_unit = '/' + self.this_unit_name

        self.input_server = None
        self.input_reply_client = None
        self.input_st = None

        self.output_client = None
        self.output_reply_server = None
        self.output_reply_st = None

        if self.osc_config.slave_enabled == 'yes' and self.osc_config.master_enabled == 'yes' and self.listen_port == self.reply_listen_port:
            # The two listen ports are the same so use one server for input and output

            #start the client that sends commands to the slaves
            self.output_client = OSC.OSCClient()
            self.mon.log(
                self, 'sending commands to slaves and replies to master on: ' +
                self.reply_listen_port)

            #start the input+output reply server
            self.mon.log(
                self,
                'listen to commands and replies from slave units using: ' +
                self.this_unit_ip + ':' + self.reply_listen_port)
            self.output_reply_server = myOSCServer(
                (self.this_unit_ip, int(self.reply_listen_port)),
                self.output_client)
            self.add_default_handler(self.output_reply_server)
            self.add_input_handlers(self.output_reply_server)
            self.add_output_reply_handlers(self.output_reply_server)

            self.input_server = self.output_reply_server

        else:

            if self.osc_config.slave_enabled == 'yes':
                # we want this to be a slave to something else

                # start the client that sends replies to controlling unit
                self.input_reply_client = OSC.OSCClient()

                #start the input server
                self.mon.log(
                    self, 'listening to commands on: ' + self.this_unit_ip +
                    ':' + self.listen_port)
                self.input_server = myOSCServer(
                    (self.this_unit_ip, int(self.listen_port)),
                    self.input_reply_client)
                self.add_default_handler(self.input_server)
                self.add_input_handlers(self.input_server)
                # print self.pretty_list(self.input_server.getOSCAddressSpace(),'\n')

            if self.osc_config.master_enabled == 'yes':
                #we want to control other units

                #start the client that sends commands to the slaves
                self.output_client = OSC.OSCClient()
                self.mon.log(
                    self, 'sending commands to slaves on port: ' +
                    self.reply_listen_port)

                #start the output reply server
                self.mon.log(
                    self, 'listen to replies from slave units using: ' +
                    self.this_unit_ip + ':' + self.reply_listen_port)
                self.output_reply_server = myOSCServer(
                    (self.this_unit_ip, int(self.reply_listen_port)),
                    self.output_client)
                self.add_default_handler(self.output_reply_server)
                self.add_output_reply_handlers(self.output_reply_server)

        return 'normal', 'osc.cfg read'

    def terminate(self):
        if self.input_server != None:
            self.input_server.close()
        if self.output_reply_server != None:
            self.output_reply_server.close()
        self.mon.log(self, 'Waiting for Server threads to finish')
        if self.input_st != None:
            self.input_st.join()  ##!!!
        if self.output_reply_st != None:
            self.output_reply_st.join()  ##!!!
        self.mon.log(self, 'server threads closed')
        if self.input_reply_client != None:
            self.input_reply_client.close()
        if self.output_client != None:
            self.output_client.close()

    def start_server(self):
        # Start input Server
        self.mon.log(self, 'Starting input OSCServer')
        if self.input_server != None:
            self.input_st = threading.Thread(
                target=self.input_server.serve_forever)
            self.input_st.start()

        # Start output_reply server
        self.mon.log(self, 'Starting output reply OSCServer')
        if self.output_reply_server != None:
            self.output_reply_st = threading.Thread(
                target=self.output_reply_server.serve_forever)
            self.output_reply_st.start()

    def parse_slaves(self):
        name_list = self.osc_config.slave_units_name.split()
        ip_list = self.osc_config.slave_units_ip.split()
        if len(name_list) == 0:
            return 'error', 'OSC Config - List of slaves name is empty'
        if len(name_list) != len(ip_list):
            return 'error', 'OSC Config - Lengths of list of slaves name and slaves IP is different'
        self.slave_name_list = []
        self.slave_ip_list = []
        for i, name in enumerate(name_list):
            self.slave_name_list.append(name)
            self.slave_ip_list.append(ip_list[i])
        return 'normal', 'slaves parsed'

    def parse_osc_command(self, fields):
        # send message to slave unit - INTERFACE WITH pipresents
        if len(fields) < 2:
            return 'error', 'too few fields in OSC command ' + ' '.join(fields)
        to_unit_name = fields[0]
        show_command = fields[1]
        # print 'FIELDS ',fields

        # send an arbitary osc message
        if show_command == 'send':
            if len(fields) > 2:
                osc_address = fields[2]
                arg_list = []
                if len(fields) > 3:
                    arg_list = fields[3:]
            else:
                return 'error', 'OSC - wrong nmber of fields in ' + ' '.join(
                    fields)

        elif show_command in ('open', 'close', 'openexclusive'):
            if len(fields) == 3:
                osc_address = self.prefix + '/' + to_unit_name + '/core/' + show_command
                arg_list = [fields[2]]
            else:
                return 'error', 'OSC - wrong number of fields in ' + ' '.join(
                    fields)

        elif show_command == 'monitor':
            if fields[2] in ('on', 'off'):
                osc_address = self.prefix + '/' + to_unit_name + '/core/' + show_command
                arg_list = [fields[2]]
            else:
                self.mon.err(
                    self,
                    'OSC - illegal state in ' + show_command + ' ' + fields[2])

        elif show_command == 'event':
            if len(fields) == 3:
                osc_address = self.prefix + '/' + to_unit_name + '/core/' + show_command
                arg_list = [fields[2]]

        elif show_command == 'animate':
            if len(fields) > 2:
                osc_address = self.prefix + '/' + to_unit_name + '/core/' + show_command
                arg_list = fields[2:]
            else:
                return 'error', 'OSC - wrong nmber of fields in ' + ' '.join(
                    fields)

        elif show_command in ('closeall', 'exitpipresents', 'shutdownnow',
                              'reboot'):
            if len(fields) == 2:
                osc_address = self.prefix + '/' + to_unit_name + '/core/' + show_command
                arg_list = []
            else:
                return 'error', 'OSC - wrong nmber of fields in ' + ' '.join(
                    fields)

        elif show_command in ('loopback', 'server-info'):
            if len(fields) == 2:
                osc_address = self.prefix + '/' + to_unit_name + '/system/' + show_command
                arg_list = []
            else:
                return 'error', 'OSC - wrong nmber of fields in ' + ' '.join(
                    fields)

        else:
            return 'error', 'OSC - unkown command in ' + ' '.join(fields)

        ip = self.find_ip(to_unit_name, self.slave_name_list,
                          self.slave_ip_list)
        if ip == '':
            return 'warn', 'OSC Unit Name not in the list of slaves: ' + to_unit_name
        self.sendto(ip, osc_address, arg_list)
        return 'normal', 'osc command sent'

    def find_ip(self, name, name_list, ip_list):
        i = 0
        for j in name_list:
            if j == name:
                break
            i = i + 1

        if i == len(name_list):
            return ''
        else:
            return ip_list[i]

    def sendto(self, ip, osc_address, arg_list):
        # print ip,osc_address,arg_list
        if self.output_client is None:
            self.mon.warn(self, 'Master not enabled, ignoring OSC command')
            return
        msg = OSC.OSCMessage()
        # print address
        msg.setAddress(osc_address)
        for arg in arg_list:
            # print arg
            msg.append(arg)

        try:
            self.output_client.sendto(msg, (ip, int(self.reply_listen_port)))
            self.mon.log(
                self,
                'Sent OSC command: ' + osc_address + ' ' + ' '.join(arg_list) +
                ' to ' + ip + ':' + self.reply_listen_port)
        except Exception as e:
            self.mon.warn(
                self, 'error in client when sending OSC command: ' + str(e))

# **************************************
# Handlers for fallback
# **************************************

    def add_default_handler(self, server):
        server.addMsgHandler('default', self.no_match_handler)

    def no_match_handler(self, addr, tags, stuff, source):
        text = "No handler for message from %s" % OSC.getUrlStr(source) + '\n'
        text += "     %s" % addr + self.pretty_list(stuff, '')
        self.mon.warn(self, text)
        return None

# **************************************
# Handlers for Slave (input)
# **************************************

    def add_input_handlers(self, server):
        server.addMsgHandler(
            self.prefix + self.this_unit + "/system/server-info",
            self.server_info_handler)
        server.addMsgHandler(self.prefix + self.this_unit + "/system/loopback",
                             self.loopback_handler)
        server.addMsgHandler(self.prefix + self.this_unit + '/core/open',
                             self.open_show_handler)
        server.addMsgHandler(self.prefix + self.this_unit + '/core/close',
                             self.close_show_handler)
        server.addMsgHandler(
            self.prefix + self.this_unit + '/core/openexclusive',
            self.openexclusive_handler)
        server.addMsgHandler(self.prefix + self.this_unit + '/core/closeall',
                             self.closeall_handler)
        server.addMsgHandler(
            self.prefix + self.this_unit + '/core/exitpipresents',
            self.exitpipresents_handler)
        server.addMsgHandler(
            self.prefix + self.this_unit + '/core/shutdownnow',
            self.shutdownnow_handler)
        server.addMsgHandler(self.prefix + self.this_unit + '/core/reboot',
                             self.reboot_handler)
        server.addMsgHandler(self.prefix + self.this_unit + '/core/event',
                             self.input_event_handler)
        server.addMsgHandler(self.prefix + self.this_unit + '/core/animate',
                             self.animate_handler)
        server.addMsgHandler(self.prefix + self.this_unit + '/core/monitor',
                             self.monitor_handler)

    # reply to master unit with name of this unit and commands
    def server_info_handler(self, addr, tags, stuff, source):

        msg = OSC.OSCMessage(self.prefix + '/system/server-info-reply')
        msg.append(self.this_unit_name)
        msg.append(self.input_server.getOSCAddressSpace())
        self.mon.log(self,
                     'Sent Server Info reply to %s:' % OSC.getUrlStr(source))
        return msg

    # reply to master unit with a loopback message
    def loopback_handler(self, addr, tags, stuff, source):
        msg = OSC.OSCMessage(self.prefix + '/system/loopback-reply')
        self.mon.log(self,
                     'Sent loopback reply to %s:' % OSC.getUrlStr(source))
        return msg

    def open_show_handler(self, address, tags, args, source):
        self.prepare_show_command_callback('open ', args, 1)

    def openexclusive_handler(self, address, tags, args, source):
        self.prepare_show_command_callback('openexclusive ', args, 1)

    def close_show_handler(self, address, tags, args, source):
        self.prepare_show_command_callback('close ', args, 1)

    def closeall_handler(self, address, tags, args, source):
        self.prepare_show_command_callback('closeall', args, 0)

    def monitor_handler(self, address, tags, args, source):
        self.prepare_show_command_callback('monitor ', args, 1)

    def exitpipresents_handler(self, address, tags, args, source):
        self.prepare_show_command_callback('exitpipresents', args, 0)

    def reboot_handler(self, address, tags, args, source):
        self.prepare_show_command_callback('reboot', args, 0)

    def shutdownnow_handler(self, address, tags, args, source):
        self.prepare_show_command_callback('shutdownnow', args, 0)

    def prepare_show_command_callback(self, command, args, limit):
        if len(args) == limit:
            if limit != 0:
                self.mon.sched(self, TimeOfDay.now,
                               'Received from OSC: ' + command + ' ' + args[0])
                self.show_command_callback(command + args[0])
            else:
                self.mon.sched(self, TimeOfDay.now,
                               'Received from OSC: ' + command)
                self.show_command_callback(command)
        else:
            self.mon.warn(
                self, 'OSC show command does not have ' + limit +
                ' argument - ignoring')

    def input_event_handler(self, address, tags, args, source):
        if len(args) == 1:
            self.input_event_callback(args[0], 'OSC')
        else:
            self.mon.warn(
                self, 'OSC input event does not have 1 argument - ignoring')

    def animate_handler(self, address, tags, args, source):
        if len(args) != 0:
            # delay symbol,param_type,param_values,req_time as a string
            text = '0 '
            for arg in args:
                text = text + arg + ' '
            text = text + '0'
            # print text
            self.animate_callback(text)
        else:
            self.mon.warn(self, 'OSC output event has no arguments - ignoring')


# **************************************
# Handlers for Master- replies from slaves (output)
# **************************************

# reply handlers do not have the destinatuion unit in the address as they are always sent to the originator

    def add_output_reply_handlers(self, server):
        server.addMsgHandler(self.prefix + "/system/server-info-reply",
                             self.server_info_reply_handler)
        server.addMsgHandler(self.prefix + "/system/loopback-reply",
                             self.loopback_reply_handler)

    # print result of info request from slave unit
    def server_info_reply_handler(self, addr, tags, stuff, source):
        self.mon.log(
            self, 'server info reply from slave ' + OSC.getUrlStr(source) +
            self.pretty_list(stuff, '\n'))
        print 'Received reply to Server-Info command from slave: ', OSC.getUrlStr(
            source), self.pretty_list(stuff, '\n')
        return None

    #print result of info request from slave unit
    def loopback_reply_handler(self, addr, tags, stuff, source):
        self.mon.log(
            self, 'server info reply from slave ' + OSC.getUrlStr(source) +
            self.pretty_list(stuff, '\n'))
        print 'Received reply to Loopback command from slave: ' + OSC.getUrlStr(
            source) + ' ' + self.pretty_list(stuff, '\n')
        return None

    def pretty_list(self, fields, separator):
        text = ' '
        for field in fields:
            text += str(field) + separator
        return text + '\n'
Пример #5
0
class OSCMonitor(object):

    def __init__(self):
    
        self.editor_issue="1.3"

        # get command options
        self.command_options=remote_options()

        # get directory holding the code
        self.pp_dir=sys.path[0]
            
        if not os.path.exists(self.pp_dir+os.sep+"pipresents.py"):
            tkMessageBox.showwarning("Pi Presents","Bad Application Directory")
            exit()
            
          
        # Initialise logging
        Monitor.log_path=self.pp_dir
        self.mon=Monitor()
        self.mon.init()
        
        Monitor.classes  = ['OSCMonitor','OSCConfig','OSCEditor']

        Monitor.log_level = int(self.command_options['debug'])

        self.mon.log (self, "Pi Presents Monitor is starting")
        self.mon.log (self," OS and separator " + os.name +'  ' + os.sep)
        self.mon.log(self,"sys.path[0] -  location of code: code "+sys.path[0])

        self.setup_gui()

        # initialise OSC config class
        self.osc_config=OSCConfig()

        self.init()

        #and start the system
        self.root.after(1000,self.run_app)
        self.root.mainloop()


    def init(self):
        # read the options and allow their editing
        self.osc_config_file = self.pp_dir + os.sep + 'pp_config' + os.sep + 'pp_oscmonitor.cfg'
        self.read_create_osc()


    def add_status(self,text):
        self.status_display.insert(END,text+'\n')
        self.status_display.see(END)

    def run_app(self):
        self.client=None
        self.server=None
        self.st=None
        
        # initialise OSC variables
        self.prefix='/pipresents'
        self.this_unit='/' + self.osc_config.this_unit_name
        self.add_status('this unit is: '+self.this_unit)
        self.controlled_by_unit='/'+self.osc_config.controlled_by_name
        self.add_status('controlled by unit : '+self.controlled_by_unit)
       
        #connect client for replies then start server to listen for commands
        self.client = OSC.OSCClient()
        self.add_status('connecting to controlled by unit: '+self.osc_config.controlled_by_ip+':'+self.osc_config.controlled_by_port +' '+self.osc_config.controlled_by_name)
        self.client.connect((self.osc_config.controlled_by_ip,int(self.osc_config.controlled_by_port)))
        self.add_status('listening for commands on:'+self.osc_config.this_unit_ip+':'+self.osc_config.this_unit_port)
        self.init_server(self.osc_config.this_unit_ip,self.osc_config.this_unit_port,self.client)
        self.add_initial_handlers()
        self.start_server()



    # ***************************************
    # OSC CLIENT TO SEND REPLIES
    # ***************************************

    def disconnect_client(self):
        if self.client != None:
            self.client.close()
        return


    # ***************************************
    # OSC SERVER TO LISTEN TO COMMANDS
    # ***************************************

    def init_server(self,ip,port_text,client):
        self.add_status('Init Server: '+ip+':'+port_text)
        self.server = myOSCServer((ip,int(port_text)),client)

    def start_server(self):
        self.add_status('Start Server')
        self.st = threading.Thread( target = self.server.serve_forever )
        self.st.start()

    def close_server(self):
        if self.server != None:
            self.server.close()
        self.mon.log(self, 'Waiting for Server-thread to finish')
        if self.st != None:
            self.st.join() ##!!!
        self.mon.log(self,'server thread closed')


    def add_initial_handlers(self):
        pass
        self.server.addMsgHandler('default', self.no_match_handler)
        self.server.addMsgHandler(self.prefix+self.this_unit+"/system/server-info", self.server_info_handler)
        self.server.addMsgHandler(self.prefix+self.this_unit+"/system/loopback", self.loopback_handler)


    def no_match_handler(self,addr, tags, stuff, source):
        text= "Message from %s" % OSC.getUrlStr(source)+'\n'
        text+= "     %s" % addr+ self.pretty_list(stuff)
        self.add_status(text+'\n')


    def server_info_handler(self,addr, tags, stuff, source):
        msg = OSC.OSCMessage(self.prefix+self.controlled_by_unit+'/system/server-info-reply')
        msg.append('Unit: '+ self.osc_config.this_unit_name)
        self.add_status('Server Info Request from %s:' % OSC.getUrlStr(source))           
        return msg


    def loopback_handler(self,addr, tags, stuff, source):
         # send a reply to the client.
        msg = OSC.OSCMessage(self.prefix+self.controlled_by_unit+'/system/loopback-reply')
        self.add_status('Loopback Request from %s:' % OSC.getUrlStr(source))
        return msg      



    def pretty_list(self,fields):
        text=' '
        for field in fields:
            text += str(field) + ' '
        return text


    # ***************************************
    # INIT EXIT MISC
    # ***************************************


    def e_edit_osc(self):
        self.disconnect_client()
        self.close_server()
        self.edit_osc()
        self.init()
        self.add_status('\n\n\nRESTART')
        self.run_app()


    def app_exit(self):
        self.disconnect_client()
        self.close_server()
        if self.root is not None:
            self.root.destroy()
        self.mon.finish()
        sys.exit() 


    def show_help (self):
        tkMessageBox.showinfo("Help","Read 'manual.pdf'")
  

    def about (self):
        tkMessageBox.showinfo("About","Simple Remote Monitor for Pi Presents\n"
                              +"Author: Ken Thompson"
                              +"\nWebsite: http://pipresents.wordpress.com/")


    def setup_gui(self):
        # set up the gui
 
        # root is the Tkinter root widget
        self.root = Tk()
        self.root.title("Remote Monitor for Pi Presents")

        # self.root.configure(background='grey')

        self.root.resizable(False,False)

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

        # bind some display fields
        self.filename = StringVar()
        self.display_show = StringVar()
        self.results = StringVar()
        self.status = StringVar()

        # define menu
        menubar = Menu(self.root)

        toolsmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='Tools', menu = toolsmenu)

        osc_configmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='Options', menu = osc_configmenu)
        osc_configmenu.add_command(label='Edit', command = self.e_edit_osc)

        helpmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='Help', menu = helpmenu)
        helpmenu.add_command(label='Help', command = self.show_help)
        helpmenu.add_command(label='About', command = self.about)
         
        self.root.config(menu=menubar)

        # status_frame      
        status_frame=Frame(self.root,padx=5,pady=5)
        status_frame.pack(side=TOP, fill=BOTH, expand=1)
        status_label = Label(status_frame, text="Status",font="arial 12 bold")
        status_label.pack(side=LEFT)
        scrollbar = Scrollbar(status_frame, orient=VERTICAL)
        self.status_display=Text(status_frame,height=10, yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.status_display.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.status_display.pack(side=LEFT,fill=BOTH, expand=1)


# ***************************************
#  OSC CONFIGURATION
# ***************************************

    def read_create_osc(self):
        if self.osc_config.read(self.osc_config_file) is False:
            self.osc_config.create(self.osc_config_file)
            eosc = OSCEditor(self.root, self.osc_config_file,'slave','Create OSC Monitor Configuration')
            self.osc_config.read(self.osc_config_file)


    def edit_osc(self):
        if self.osc_config.read(self.osc_config_file) is False:
            self.osc_config.create(self.osc_config_file)
        eosc = OSCEditor(self.root, self.osc_config_file,'slave','Edit OSC Monitor Configuration')
Пример #6
0
class PPEditor(object):

    # ***************************************
    # INIT
    # ***************************************

    def __init__(self):
    
        self.editor_issue="1.3"

        # get command options
        self.command_options=ed_options()

        # get directory holding the code
        self.pp_dir=sys.path[0]
            
        if not os.path.exists(self.pp_dir+os.sep+"pp_editor.py"):
            tkMessageBox.showwarning("Pi Presents","Bad Application Directory")
            exit()
            
          
        # Initialise logging
        Monitor.log_path=self.pp_dir
        self.mon=Monitor()
        self.mon.init()
        
        Monitor.classes  = ['PPEditor','EditItem','Validator']

        Monitor.log_level = int(self.command_options['debug'])

        self.mon.log (self, "Pi Presents Editor is starting")
        self.mon.log (self," OS and separator " + os.name +'  ' + os.sep)
        self.mon.log(self,"sys.path[0] -  location of code: code "+sys.path[0])


        # set up the gui
 
        # root is the Tkinter root widget
        self.root = Tk()
        self.root.title("Editor for Pi Presents")

        # self.root.configure(background='grey')

        self.root.resizable(False,False)

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

        # bind some display fields
        self.filename = StringVar()
        self.display_selected_track_title = StringVar()
        self.display_show = StringVar()


        # define menu
        menubar = Menu(self.root)

        profilemenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        profilemenu.add_command(label='Open', command = self.open_existing_profile)
        profilemenu.add_command(label='Validate', command = self.validate_profile)
        menubar.add_cascade(label='Profile', menu = profilemenu)

        ptypemenu = Menu(profilemenu, tearoff=0, bg="grey", fg="black")
        ptypemenu.add_command(label='Exhibit', command = self.new_exhibit_profile)
        ptypemenu.add_command(label='Media Show', command = self.new_mediashow_profile)
        ptypemenu.add_command(label='Art Media Show', command = self.new_artmediashow_profile)
        ptypemenu.add_command(label='Menu', command = self.new_menu_profile)
        ptypemenu.add_command(label='Presentation', command = self.new_presentation_profile)
        ptypemenu.add_command(label='Interactive', command = self.new_interactive_profile)
        ptypemenu.add_command(label='Live Show', command = self.new_liveshow_profile)
        ptypemenu.add_command(label='Art Live Show', command = self.new_artliveshow_profile)
        ptypemenu.add_command(label='RadioButton Show', command = self.new_radiobuttonshow_profile)
        ptypemenu.add_command(label='Hyperlink Show', command = self.new_hyperlinkshow_profile)
        ptypemenu.add_command(label='Blank', command = self.new_blank_profile)
        profilemenu.add_cascade(label='New from Template', menu = ptypemenu)
        
        showmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        showmenu.add_command(label='Delete', command = self.remove_show)
        showmenu.add_command(label='Edit', command = self.m_edit_show)
        showmenu.add_command(label='Copy To', command = self.copy_show)
        menubar.add_cascade(label='Show', menu = showmenu)

        stypemenu = Menu(showmenu, tearoff=0, bg="grey", fg="black")
        stypemenu.add_command(label='Menu', command = self.add_menushow)
        stypemenu.add_command(label='MediaShow', command = self.add_mediashow)
        stypemenu.add_command(label='LiveShow', command = self.add_liveshow)
        stypemenu.add_command(label='HyperlinkShow', command = self.add_hyperlinkshow)
        stypemenu.add_command(label='RadioButtonShow', command = self.add_radiobuttonshow)
        stypemenu.add_command(label='ArtMediaShow', command = self.add_artmediashow)
        stypemenu.add_command(label='ArtLiveShow', command = self.add_artliveshow)
        showmenu.add_cascade(label='Add', menu = stypemenu)
        
        medialistmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='MediaList', menu = medialistmenu)
        medialistmenu.add_command(label='Add', command = self.add_medialist)
        medialistmenu.add_command(label='Delete', command = self.remove_medialist)
        medialistmenu.add_command(label='Copy To', command = self.copy_medialist)
      
        trackmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        trackmenu.add_command(label='Delete', command = self.remove_track)
        trackmenu.add_command(label='Edit', command = self.m_edit_track)
        trackmenu.add_command(label='Add from Dir', command = self.add_tracks_from_dir)
        trackmenu.add_command(label='Add from File', command = self.add_track_from_file)


        menubar.add_cascade(label='Track', menu = trackmenu)

        typemenu = Menu(trackmenu, tearoff=0, bg="grey", fg="black")
        typemenu.add_command(label='Video', command = self.new_video_track)
        typemenu.add_command(label='Audio', command = self.new_audio_track)
        typemenu.add_command(label='Image', command = self.new_image_track)
        typemenu.add_command(label='Web', command = self.new_web_track)
        typemenu.add_command(label='Message', command = self.new_message_track)
        typemenu.add_command(label='Show', command = self.new_show_track)
        typemenu.add_command(label='Menu Track', command = self.new_menu_track)
        trackmenu.add_cascade(label='New', menu = typemenu)

        oscmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='OSC', menu = oscmenu)
        oscmenu.add_command(label='Create OSC configuration', command = self.create_osc)
        oscmenu.add_command(label='Edit OSC Configuration', command = self.edit_osc)
        oscmenu.add_command(label='Delete OSC Configuration', command = self.delete_osc)


        toolsmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='Tools', menu = toolsmenu)
        toolsmenu.add_command(label='Update All', command = self.update_all)
        
        optionsmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='Options', menu = optionsmenu)
        optionsmenu.add_command(label='Edit', command = self.edit_options)

        helpmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='Help', menu = helpmenu)
        helpmenu.add_command(label='Help', command = self.show_help)
        helpmenu.add_command(label='About', command = self.about)
         
        self.root.config(menu=menubar)

        top_frame=Frame(self.root)
        top_frame.pack(side=TOP)
        bottom_frame=Frame(self.root)
        bottom_frame.pack(side=TOP, fill=BOTH, expand=1)        

        left_frame=Frame(bottom_frame, padx=5)
        left_frame.pack(side=LEFT)
        middle_frame=Frame(bottom_frame,padx=5)
        middle_frame.pack(side=LEFT)              
        right_frame=Frame(bottom_frame,padx=5,pady=10)
        right_frame.pack(side=LEFT)
        updown_frame=Frame(bottom_frame,padx=5)
        updown_frame.pack(side=LEFT)
        
        tracks_title_frame=Frame(right_frame)
        tracks_title_frame.pack(side=TOP)
        tracks_label = Label(tracks_title_frame, text="Tracks in Selected Medialist")
        tracks_label.pack()
        tracks_frame=Frame(right_frame)
        tracks_frame.pack(side=TOP)
        shows_title_frame=Frame(left_frame)
        shows_title_frame.pack(side=TOP)
        shows_label = Label(shows_title_frame, text="Shows")
        shows_label.pack()
        shows_frame=Frame(left_frame)
        shows_frame.pack(side=TOP)
        shows_title_frame=Frame(left_frame)
        shows_title_frame.pack(side=TOP)
        medialists_title_frame=Frame(left_frame)
        medialists_title_frame.pack(side=TOP)
        medialists_label = Label(medialists_title_frame, text="Medialists")
        medialists_label.pack()
        medialists_frame=Frame(left_frame)
        medialists_frame.pack(side=LEFT)
        
        # define buttons 

        add_button = Button(middle_frame, width = 5, height = 2, text='Edit\nShow',
                            fg='black', command = self.m_edit_show, bg="light grey")
        add_button.pack(side=RIGHT)
        
        add_button = Button(updown_frame, width = 5, height = 1, text='Add',
                            fg='black', command = self.add_track_from_file, bg="light grey")
        add_button.pack(side=TOP)
        add_button = Button(updown_frame, width = 5, height = 1, text='Edit',
                            fg='black', command = self.m_edit_track, bg="light grey")
        add_button.pack(side=TOP)
        add_button = Button(updown_frame, width = 5, height = 1, text='Up',
                            fg='black', command = self.move_track_up, bg="light grey")
        add_button.pack(side=TOP)
        add_button = Button(updown_frame, width = 5, height = 1, text='Down',
                            fg='black', command = self.move_track_down, bg="light grey")
        add_button.pack(side=TOP)


        # define display of showlist 
        scrollbar = Scrollbar(shows_frame, orient=VERTICAL)
        self.shows_display = Listbox(shows_frame, selectmode=SINGLE, height=12,
                                     width = 40, bg="white",activestyle=NONE,
                                     fg="black", yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.shows_display.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.shows_display.pack(side=LEFT, fill=BOTH, expand=1)
        self.shows_display.bind("<ButtonRelease-1>", self.e_select_show)

    
        # define display of medialists
        scrollbar = Scrollbar(medialists_frame, orient=VERTICAL)
        self.medialists_display = Listbox(medialists_frame, selectmode=SINGLE, height=12,
                                          width = 40, bg="white",activestyle=NONE,
                                          fg="black",yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.medialists_display.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.medialists_display.pack(side=LEFT,  fill=BOTH, expand=1)
        self.medialists_display.bind("<ButtonRelease-1>", self.select_medialist)


        # define display of tracks
        scrollbar = Scrollbar(tracks_frame, orient=VERTICAL)
        self.tracks_display = Listbox(tracks_frame, selectmode=SINGLE, height=25,
                                      width = 40, bg="white",activestyle=NONE,
                                      fg="black",yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.tracks_display.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.tracks_display.pack(side=LEFT,fill=BOTH, expand=1)
        self.tracks_display.bind("<ButtonRelease-1>", self.e_select_track)


        # initialise editor options class and OSC config class
        self.options=Options(self.pp_dir) # creates options file in code directory if necessary
        self.osc_config=OSCConfig()
        
        # initialise variables      
        self.init()
        
        # and enter Tkinter event loop
        self.root.mainloop()        


    # ***************************************
    # INIT AND EXIT
    # ***************************************
    def app_exit(self):
        self.root.destroy()
        exit()


    def init(self):
        self.options.read()
        self.pp_home_dir = self.options.pp_home_dir
        self.pp_profiles_offset = self.options.pp_profiles_offset
        self.initial_media_dir = self.options.initial_media_dir
        self.mon.log(self,"Data Home from options is "+self.pp_home_dir)
        self.mon.log(self,"Current Profiles Offset from options is "+self.pp_profiles_offset)
        self.mon.log(self,"Initial Media from options is "+self.initial_media_dir)
        self.pp_profile_dir=''
        self.osc_config_file = ''
        self.current_medialist=None
        self.current_showlist=None
        self.current_show=None
        self.shows_display.delete(0,END)
        self.medialists_display.delete(0,END)
        self.tracks_display.delete(0,END)



    # ***************************************
    # MISCELLANEOUS
    # ***************************************

    def edit_options(self):
        """edit the options then read them from file"""
        eo = OptionsDialog(self.root, self.options.options_file,'Edit Options')
        if eo.result is True: self.init()



    def show_help (self):
        tkMessageBox.showinfo("Help","Read 'manual.pdf'")
  

    def about (self):
        tkMessageBox.showinfo("About","Editor for Pi Presents Profiles\n"
                              +"For profile version: " + self.editor_issue + "\nAuthor: Ken Thompson"
                              +"\nWebsite: http://pipresents.wordpress.com/")

    def validate_profile(self):
        val =Validator()
        val.validate_profile(self.root,self.pp_dir,self.pp_home_dir,self.pp_profile_dir,self.editor_issue,True)


    # **************
    # OSC CONFIGURATION
    # **************

    def create_osc(self):
        if self.pp_profile_dir=='':
            return
        if self.osc_config.read(self.osc_config_file) is False:
            iodir=self.pp_profile_dir+os.sep+'pp_io_config'
            if not os.path.exists(iodir):
                os.makedirs(iodir)
            self.osc_config.create(self.osc_config_file)

    def edit_osc(self):
        if self.osc_config.read(self.osc_config_file) is False:
            # print 'no config file'
            return
        osc_ut=OSCUnitType(self.root,self.osc_config.this_unit_type)
        self.req_unit_type=osc_ut.result
        if self.req_unit_type != None:
            # print self.req_unit_type
            eosc = OSCEditor(self.root, self.osc_config_file,self.req_unit_type,'Edit OSC Configuration')
            
    def delete_osc(self):
        if self.osc_config.read(self.osc_config_file) is False:
            return
        os.rename(self.osc_config_file,self.osc_config_file+'.bak')
        

    
    # **************
    # PROFILES
    # **************

    def open_existing_profile(self):
        initial_dir=self.pp_home_dir+os.sep+"pp_profiles"+self.pp_profiles_offset
        if os.path.exists(initial_dir) is False:
            self.mon.err(self,"Profiles directory not found: " + initial_dir + "\n\nHint: Data Home option must end in pp_home")
            return
        dir_path=tkFileDialog.askdirectory(initialdir=initial_dir)
        # dir_path="C:\Users\Ken\pp_home\pp_profiles\\ttt"
        if len(dir_path)>0:
            self.open_profile(dir_path)
        

    def open_profile(self,dir_path):
        showlist_file = dir_path + os.sep + "pp_showlist.json"
        if os.path.exists(showlist_file) is False:
            self.mon.err(self,"Not a Profile: " + dir_path + "\n\nHint: Have you opened the profile directory?")
            return
        self.pp_profile_dir = dir_path
        self.root.title("Editor for Pi Presents - "+ self.pp_profile_dir)
        if self.open_showlist(self.pp_profile_dir) is False:
            self.init()
            return
        self.open_medialists(self.pp_profile_dir)
        self.refresh_tracks_display()
        self.osc_config_file=self.pp_profile_dir+os.sep+'pp_io_config'+os.sep+'osc.cfg'


    def new_profile(self,profile):
        d = Edit1Dialog(self.root,"New Profile","Name", "")
        if d .result  is  None:
            return
        name=str(d.result)
        if name == "":
            tkMessageBox.showwarning("New Profile","Name is blank")
            return
        to = self.pp_home_dir + os.sep + "pp_profiles"+ self.pp_profiles_offset + os.sep + name
        if os.path.exists(to) is  True:
            tkMessageBox.showwarning( "New Profile","Profile exists\n(%s)" % to )
            return
        shutil.copytree(profile, to, symlinks=False, ignore=None)
        self.open_profile(to)


        
    def new_exhibit_profile(self):
        profile = self.pp_dir+os.sep+'pp_resources'+os.sep+'pp_templates'+os.sep + 'ppt_exhibit_1p3'
        self.new_profile(profile)

    def new_interactive_profile(self):
        profile = self.pp_dir+os.sep+'pp_resources'+os.sep+'pp_templates'+os.sep + 'ppt_interactive_1p3'
        self.new_profile(profile)

    def new_menu_profile(self):
        profile = self.pp_dir+os.sep+'pp_resources'+os.sep+'pp_templates'+os.sep + 'ppt_menu_1p3'
        self.new_profile(profile)

    def new_presentation_profile(self):
        profile = self.pp_dir+os.sep+'pp_resources'+os.sep+'pp_templates'+os.sep + 'ppt_presentation_1p3'
        self.new_profile(profile)

    def new_blank_profile(self):
        profile = self.pp_dir+os.sep+'pp_resources'+os.sep+'pp_templates'+os.sep +"ppt_blank_1p3"
        self.new_profile(profile)

    def new_mediashow_profile(self):
        profile = self.pp_dir+os.sep+'pp_resources'+os.sep+'pp_templates'+os.sep + 'ppt_mediashow_1p3'
        self.new_profile(profile)
        
    def new_liveshow_profile(self):
        profile = self.pp_dir+os.sep+'pp_resources'+os.sep+'pp_templates'+os.sep + 'ppt_liveshow_1p3'
        self.new_profile(profile)

    def new_artmediashow_profile(self):
        profile = self.pp_dir+os.sep+'pp_resources'+os.sep+'pp_templates'+os.sep + 'ppt_artmediashow_1p3'
        self.new_profile(profile)
        
    def new_artliveshow_profile(self):
        profile = self.pp_dir+os.sep+'pp_resources'+os.sep+'pp_templates'+os.sep + 'ppt_artliveshow_1p3'
        self.new_profile(profile)

    def new_radiobuttonshow_profile(self):
        profile = self.pp_dir+os.sep+'pp_resources'+os.sep+'pp_templates'+os.sep + 'ppt_radiobuttonshow_1p3'
        self.new_profile(profile)

    def new_hyperlinkshow_profile(self):
        profile = self.pp_dir+os.sep+'pp_resources'+os.sep+'pp_templates'+os.sep + 'ppt_hyperlinkshow_1p3'
        self.new_profile(profile)

    # ***************************************
    # Shows
    # ***************************************

    def open_showlist(self,profile_dir):
        showlist_file = profile_dir + os.sep + "pp_showlist.json"
        if os.path.exists(showlist_file) is False:
            self.mon.err(self,"showlist file not found at " + profile_dir + "\n\nHint: Have you opened the profile directory?")
            self.app_exit()
        self.current_showlist=ShowList()
        self.current_showlist.open_json(showlist_file)
        if float(self.current_showlist.sissue())<float(self.editor_issue) or  (self.command_options['forceupdate']  is  True and float(self.current_showlist.sissue()) == float(self.editor_issue)):
            self.update_profile()
            self.mon.err(self,"Version of profile has been updated to "+self.editor_issue+", please re-open")
            return False
        if float(self.current_showlist.sissue())>float(self.editor_issue):
            self.mon.err(self,"Version of profile is greater than editor, must exit")
            self.app_exit()
        self.refresh_shows_display()
        return True


    def save_showlist(self,showlist_dir):
        if self.current_showlist is not None:
            showlist_file = showlist_dir + os.sep + "pp_showlist.json"
            self.current_showlist.save_list(showlist_file)
            
    def add_mediashow(self):
        self.add_show(PPdefinitions.new_shows['mediashow'])

    def add_liveshow(self):
        self.add_show(PPdefinitions.new_shows['liveshow'])

    def add_radiobuttonshow(self):
        self.add_show(PPdefinitions.new_shows['radiobuttonshow'])

    def add_hyperlinkshow(self):
        self.add_show(PPdefinitions.new_shows['hyperlinkshow'])

    def add_artliveshow(self):
        self.add_show(PPdefinitions.new_shows['artliveshow'])

    def add_artmediashow(self):
        self.add_show(PPdefinitions.new_shows['artmediashow'])
        
    def add_menushow(self):
        self.add_show(PPdefinitions.new_shows['menu'])

    def add_start(self):  
        self.add_show(PPdefinitions.new_shows['start'])


    def add_show(self,default):
        # append it to the showlist and then add the medialist
        if self.current_showlist is not None:
            d = Edit1Dialog(self.root,"AddShow","Show Reference", "")
            if d.result  is  None:
                return
            name=str(d.result)
            if name == "":
                tkMessageBox.showwarning("Add Show","Name is blank")
                return
                                         
            if self.current_showlist.index_of_show(name) != -1:
                tkMessageBox.showwarning("Add Show","A Show with this name already exists")
                return            
            copied_show=self.current_showlist.copy(default,name)
            mediafile=self.add_medialist(name)
            if mediafile != '':
                copied_show['medialist']=mediafile
            self.current_showlist.append(copied_show)
            self.save_showlist(self.pp_profile_dir)
            self.refresh_shows_display()

            
    def remove_show(self):
        if  self.current_showlist is not None and self.current_showlist.length()>0 and self.current_showlist.show_is_selected():
            if tkMessageBox.askokcancel("Delete Show","Delete Show"):
                index= self.current_showlist.selected_show_index()
                self.current_showlist.remove(index)
                self.save_showlist(self.pp_profile_dir)
                self.refresh_shows_display()

    def show_refs(self):
        _show_refs=[]
        for index in range(self.current_showlist.length()):
            if self.current_showlist.show(index)['show-ref'] != "start":
                _show_refs.append(copy.deepcopy(self.current_showlist.show(index)['show-ref']))
        return _show_refs
 
    def refresh_shows_display(self):
        self.shows_display.delete(0,self.shows_display.size())
        for index in range(self.current_showlist.length()):
            self.shows_display.insert(END, self.current_showlist.show(index)['title']+"   ["+self.current_showlist.show(index)['show-ref']+"]")        
        if self.current_showlist.show_is_selected():
            self.shows_display.itemconfig(self.current_showlist.selected_show_index(),fg='red')            
            self.shows_display.see(self.current_showlist.selected_show_index())

            
    def e_select_show(self,event):
        if self.current_showlist is not None and self.current_showlist.length()>0:
            mouse_item_index=int(event.widget.curselection()[0])
            self.current_showlist.select(mouse_item_index)
            self.refresh_shows_display()

    def copy_show(self):
        if  self.current_showlist is not None and self.current_showlist.show_is_selected():
            self.add_show(self.current_showlist.selected_show())

        
    def m_edit_show(self):
        self.edit_show(PPdefinitions.show_types,PPdefinitions.show_field_specs)
        
     

    def edit_show(self,show_types,field_specs):
        if self.current_showlist is not None and self.current_showlist.show_is_selected():
            d=EditItem(self.root,"Edit Show",self.current_showlist.selected_show(),show_types,field_specs,self.show_refs(),
                       self.initial_media_dir,self.pp_home_dir,'show')
            if d.result  is  True:

                self.save_showlist(self.pp_profile_dir)
                self.refresh_shows_display()

 

    # ***************************************
    #   Medialists
    # ***************************************

    def open_medialists(self,profile_dir):
        self.medialists = []
        for this_file in os.listdir(profile_dir):
            if this_file.endswith(".json") and this_file not in ('pp_showlist.json','schedule.json'):
                self.medialists = self.medialists + [this_file]
        self.medialists_display.delete(0,self.medialists_display.size())
        for index in range (len(self.medialists)):
            self.medialists_display.insert(END, self.medialists[index])
        self.current_medialists_index=-1
        self.current_medialist=None


    def add_medialist(self,name=None):
        if name is None:
            d = Edit1Dialog(self.root,"Add Medialist","File", "")
            if d.result  is  None:
                return ''
            name=str(d.result)
            if name == "":
                tkMessageBox.showwarning("Add medialist","Name is blank")
                return ''
            
        if not name.endswith(".json"):
            name=name+(".json")
                
        path = self.pp_profile_dir + os.sep + name
        if os.path.exists(path) is  True:
            tkMessageBox.showwarning("Add medialist","Medialist file exists\n(%s)" % path)
            return ''
        nfile = open(path,'wb')
        nfile.write("{")
        nfile.write("\"issue\":  \""+self.editor_issue+"\",\n")
        nfile.write("\"tracks\": [")
        nfile.write("]")
        nfile.write("}")
        nfile.close()
        # append it to the list
        self.medialists.append(copy.deepcopy(name))
        # add title to medialists display
        self.medialists_display.insert(END, name)  
        # and set it as the selected medialist
        self.refresh_medialists_display()
        return name


    def copy_medialist(self,to_file=None):
        if self.current_medialist is not None:
            #from_file= self.current_medialist 
            from_file= self.medialists[self.current_medialists_index]
            if to_file is None:
                d = Edit1Dialog(self.root,"Copy Medialist","File", "")
                if d.result  is  None:
                    return ''
                to_file=str(d.result)
                if to_file == "":
                    tkMessageBox.showwarning("Copy medialist","Name is blank")
                    return ''
                
            success_file = self.copy_medialist_file(from_file,to_file)
            if success_file =='':
                return ''

            # append it to the list
            self.medialists.append(copy.deepcopy(success_file))
            # add title to medialists display
            self.medialists_display.insert(END, success_file)
            # and reset  selected medialist
            self.current_medialist=None
            self.refresh_medialists_display()
            self.refresh_tracks_display()
            return success_file
        else:
            return ''

    def copy_medialist_file(self,from_file,to_file):
        if not to_file.endswith(".json"):
            to_file+=(".json")
                
        to_path = self.pp_profile_dir + os.sep + to_file
        if os.path.exists(to_path) is  True:
            tkMessageBox.showwarning("Copy medialist","Medialist file exists\n(%s)" % to_path)
            return ''
        
        from_path= self.pp_profile_dir + os.sep + from_file
        if os.path.exists(from_path) is  False:
            tkMessageBox.showwarning("Copy medialist","Medialist file not found\n(%s)" % from_path)
            return ''

        shutil.copy(from_path,to_path)
        return to_file


    def remove_medialist(self):
        if self.current_medialist is not None:
            if tkMessageBox.askokcancel("Delete Medialist","Delete Medialist"):
                os.remove(self.pp_profile_dir+ os.sep + self.medialists[self.current_medialists_index])
                self.open_medialists(self.pp_profile_dir)
                self.refresh_medialists_display()
                self.refresh_tracks_display()


    def select_medialist(self,event):
        """
        user clicks on a medialst in a profile so try and select it.
        """
        # needs forgiving int for possible tkinter upgrade
        if len(self.medialists)>0:
            self.current_medialists_index=int(event.widget.curselection()[0])
            self.current_medialist=MediaList('ordered')
            if not self.current_medialist.open_list(self.pp_profile_dir+ os.sep + self.medialists[self.current_medialists_index],self.current_showlist.sissue()):
                self.mon.err(self,"medialist is a different version to showlist: "+ self.medialists[self.current_medialists_index])
                self.app_exit()        
            self.refresh_tracks_display()
            self.refresh_medialists_display()


    def refresh_medialists_display(self):
        self.medialists_display.delete(0,len(self.medialists))
        for index in range (len(self.medialists)):
            self.medialists_display.insert(END, self.medialists[index])
        if self.current_medialist is not None:
            self.medialists_display.itemconfig(self.current_medialists_index,fg='red')
            self.medialists_display.see(self.current_medialists_index)

    def save_medialist(self):
        basefile=self.medialists[self.current_medialists_index]
        # print type(basefile)
        # basefile=str(basefile)
        # print type(basefile)
        medialist_file = self.pp_profile_dir+ os.sep + basefile
        self.current_medialist.save_list(medialist_file)

  
          
    # ***************************************
    #   Tracks
    # ***************************************
          
    def refresh_tracks_display(self):
        self.tracks_display.delete(0,self.tracks_display.size())
        if self.current_medialist is not None:
            for index in range(self.current_medialist.length()):
                if self.current_medialist.track(index)['track-ref'] != '':
                    track_ref_string="  ["+self.current_medialist.track(index)['track-ref']+"]"
                else:
                    track_ref_string=""
                self.tracks_display.insert(END, self.current_medialist.track(index)['title']+track_ref_string)        
            if self.current_medialist.track_is_selected():
                self.tracks_display.itemconfig(self.current_medialist.selected_track_index(),fg='red')            
                self.tracks_display.see(self.current_medialist.selected_track_index())
            
    def e_select_track(self,event):
        if self.current_medialist is not None and self.current_medialist.length()>0:
            mouse_item_index=int(event.widget.curselection()[0])
            self.current_medialist.select(mouse_item_index)
            self.refresh_tracks_display()

    def m_edit_track(self):
        self.edit_track(PPdefinitions.track_types,PPdefinitions.track_field_specs)

    def edit_track(self,track_types,field_specs):      
        if self.current_medialist is not None and self.current_medialist.track_is_selected():
            d=EditItem(self.root,"Edit Track",self.current_medialist.selected_track(),track_types,field_specs,
                       self.show_refs(),self.initial_media_dir,self.pp_home_dir,'track')
            if d.result  is  True:
                self.save_medialist()
            self.refresh_tracks_display()

    def move_track_up(self):
        if self.current_medialist is not None and self.current_medialist.track_is_selected():
            self.current_medialist.move_up()
            self.refresh_tracks_display()
            self.save_medialist()

    def move_track_down(self):
        if self.current_medialist is not None and self.current_medialist.track_is_selected():
            self.current_medialist.move_down()
            self.refresh_tracks_display()
            self.save_medialist()
        
    def new_track(self,fields,values):
        if self.current_medialist is not None:
            # print '\nfields ', fields
            # print '\nvalues ', values
            new_track=copy.deepcopy(fields)
            # print ',\new track ',new_track
            self.current_medialist.append(new_track)
            # print '\nbefore values ',self.current_medialist.print_list()
            if values is not None:
                self.current_medialist.update(self.current_medialist.length()-1,values)
            self.current_medialist.select(self.current_medialist.length()-1)
            self.refresh_tracks_display()
            self.save_medialist()

    def new_message_track(self):
        self.new_track(PPdefinitions.new_tracks['message'],None)
            
    def new_video_track(self):
        self.new_track(PPdefinitions.new_tracks['video'],None)
  
    def new_audio_track(self):
        self.new_track(PPdefinitions.new_tracks['audio'],None)

    def new_web_track(self):
        self.new_track(PPdefinitions.new_tracks['web'],None)
        
    def new_image_track(self):
        self.new_track(PPdefinitions.new_tracks['image'],None)

    def new_show_track(self):
        self.new_track(PPdefinitions.new_tracks['show'],None)

    def new_menu_track(self):
        self.new_track(PPdefinitions.new_tracks['menu'],None)
 
    def remove_track(self):
        if  self.current_medialist is not None and self.current_medialist.length()>0 and self.current_medialist.track_is_selected():
            if tkMessageBox.askokcancel("Delete Track","Delete Track"):
                index= self.current_medialist.selected_track_index()
                self.current_medialist.remove(index)
                self.save_medialist()
                self.refresh_tracks_display()
                
    def add_track_from_file(self):
        if self.current_medialist is None: return
        # print "initial directory ", self.options.initial_media_dir
        files_path=tkFileDialog.askopenfilename(initialdir=self.options.initial_media_dir, multiple=True)
        # fix for tkinter bug
        files_path =  self.root.tk.splitlist(files_path)
        for file_path in files_path:
            file_path=os.path.normpath(file_path)
            # print "file path ", file_path
            self.add_track(file_path)
        self.save_medialist()

    def add_tracks_from_dir(self):
        if self.current_medialist is None: return
        image_specs =[PPdefinitions.IMAGE_FILES,PPdefinitions.VIDEO_FILES,PPdefinitions.AUDIO_FILES,
                      PPdefinitions.WEB_FILES,('All files', '*')]
        # last one is ignored in finding files in directory, for dialog box only
        directory=tkFileDialog.askdirectory(initialdir=self.options.initial_media_dir)
        # deal with tuple returned on Cancel
        if len(directory) == 0: return
        # make list of exts we recognise
        exts = []
        for image_spec in image_specs[:-1]:
            image_list=image_spec[1:]
            for ext in image_list:
                exts.append(copy.deepcopy(ext))
        for this_file in os.listdir(directory):
            (root_file,ext_file)= os.path.splitext(this_file)
            if ext_file.lower() in exts:
                file_path=directory+os.sep+this_file
                # print "file path before ", file_path
                file_path=os.path.normpath(file_path)
                # print "file path after ", file_path
                self.add_track(file_path)
        self.save_medialist()


    def add_track(self,afile):
        relpath = os.path.relpath(afile,self.pp_home_dir)
        # print "relative path ",relpath
        common = os.path.commonprefix([afile,self.pp_home_dir])
        # print "common ",common
        if common.endswith("pp_home")  is  False:
            location = afile
        else:
            location = "+" + os.sep + relpath
            location = string.replace(location,'\\','/')
            # print "location ",location
        (root,title)=os.path.split(afile)
        (root,ext)= os.path.splitext(afile)
        if ext.lower() in PPdefinitions.IMAGE_FILES:
            self.new_track(PPdefinitions.new_tracks['image'],{'title':title,'track-ref':'','location':location})
        elif ext.lower() in PPdefinitions.VIDEO_FILES:
            self.new_track(PPdefinitions.new_tracks['video'],{'title':title,'track-ref':'','location':location})
        elif ext.lower() in PPdefinitions.AUDIO_FILES:
            self.new_track(PPdefinitions.new_tracks['audio'],{'title':title,'track-ref':'','location':location})
        elif ext.lower() in PPdefinitions.WEB_FILES:
            self.new_track(PPdefinitions.new_tracks['web'],{'title':title,'track-ref':'','location':location})
        else:
            self.mon.err(self,afile + " - cannot determine track type, use menu track>new")



    # *********************************************
    # UPDATE PROFILE
    # **********************************************

    def update_all(self):
        self.init()
        for profile_file in os.listdir(self.pp_home_dir+os.sep+'pp_profiles'+self.pp_profiles_offset):
            # self.mon.log (self,"Updating "+profile_file)
            self.pp_profile_dir = self.pp_home_dir+os.sep+'pp_profiles'+self.pp_profiles_offset + os.sep + profile_file
            if not os.path.exists(self.pp_profile_dir+os.sep+"pp_showlist.json"):
                tkMessageBox.showwarning("Pi Presents","Not a profile, skipping "+self.pp_profile_dir)
            else:
                self.current_showlist=ShowList()
                self.current_showlist.open_json(self.pp_profile_dir+os.sep+"pp_showlist.json")
                self.mon.log (self,"Version of profile "+ profile_file + ' is ' + self.current_showlist.sissue())
                if float(self.current_showlist.sissue())<float(self.editor_issue):
                    self.mon.log(self,"Version of profile "+profile_file+ "  is being updated to "+self.editor_issue)
                    self.update_profile()
                elif (self.command_options['forceupdate']  is  True and float(self.current_showlist.sissue()) == float(self.editor_issue)):
                    self.mon.log(self, "Forced updating of " + profile_file + ' to '+self.editor_issue)
                    self.update_profile()
                elif float(self.current_showlist.sissue())>float(self.editor_issue):
                    tkMessageBox.showwarning("Pi Presents", "Version of profile " +profile_file+ " is greater than editor, skipping")
                else:
                    self.mon.log(self," Skipping Profile " + profile_file + " It is already up to date ")
        self.init()
        tkMessageBox.showwarning("Pi Presents","All profiles updated")
            
    def update_profile(self):

        self.update_medialists()   # medialists and their tracks
        self.update_shows()         #shows in showlist, also creates menu tracks for 1.2>1.3
        

    def update_shows(self):
        # open showlist into a list of dictionaries
        self.mon.log (self,"Updating show ")
        ifile  = open(self.pp_profile_dir + os.sep + "pp_showlist.json", 'rb')
        shows = json.load(ifile)['shows']
        ifile.close()

        # special 1.2>1.3 create menu medialists with menu track from show
        #go through shows - if type = menu and version is greater copy its medialist to a new medialist with  name = <show-ref>-menu1p3.json
        for show in shows:
            #create a new medialist medialist != show-ref as menus can't now share medialists
            if show['type']=='menu' and float(self.current_showlist.sissue())<float(self.editor_issue):
                to_file=show['show-ref']+'-menu1p3.json'
                from_file = show['medialist']
                if to_file != from_file:
                    self.copy_medialist_file(from_file,to_file)
                else:
                    self.mon.warn(self, 'medialist file' + to_file + ' already exists, must exit with incomplete update')
                    return False

                #update the reference to the medialist
                show['medialist']=to_file
                
                #delete show fields so they are recreated with new default content
                del show['controls']
                
                # open the  medialist and add the menu track then populate some of its fields from the show
                ifile  = open(self.pp_profile_dir + os.sep + to_file, 'rb')
                tracks = json.load(ifile)['tracks']
                ifile.close()
                
                new_track=copy.deepcopy(PPdefinitions.new_tracks['menu'])
                tracks.append(copy.deepcopy(new_track))

                # copy menu parameters from menu show to menu track and init values of some              
                self.transfer_show_params(show,tracks,'menu-track',("entry-colour","entry-font", "entry-select-colour", 
                                                                    "hint-colour", "hint-font", "hint-text", "hint-x","hint-y",
                                                                    "menu-bullet", "menu-columns", "menu-direction", "menu-guidelines",
                                                                    "menu-horizontal-padding", "menu-horizontal-separation", "menu-icon-height", "menu-icon-mode",
                                                                    "menu-icon-width", "menu-rows", "menu-strip", "menu-strip-padding",  "menu-text-height", "menu-text-mode", "menu-text-width",
                                                                     "menu-vertical-padding", "menu-vertical-separation",
                                                                    "menu-window"))
                                          
                # and save the medialist
                dic={'issue':self.editor_issue,'tracks':tracks}
                ofile  = open(self.pp_profile_dir + os.sep + to_file, "wb")
                json.dump(dic,ofile,sort_keys=True,indent=1)
                # end for show in shows

        #update the fields in  all shows
        replacement_shows=self.update_shows_in_showlist(shows)
        dic={'issue':self.editor_issue,'shows':replacement_shows}
        ofile  = open(self.pp_profile_dir + os.sep + "pp_showlist.json", "wb")
        json.dump(dic,ofile,sort_keys=True,indent=1)
        return True


    def transfer_show_params(self,show,tracks,track_ref,fields):
        # find the menu track in medialist
        for index,track in enumerate(tracks):
            if track['track-ref']== 'menu-track':
                break
            
        #update some fields with new default content
        tracks[index]['links']=PPdefinitions.new_tracks['menu']['links']

        #transfer values from show to track
        for field in fields:
            tracks[index][field]=show[field]
            # print show[field], tracks[index][field]
            pass
            
        
                                          

    def update_medialists(self):
         # UPDATE MEDIALISTS AND THEIR TRACKS
        for this_file in os.listdir(self.pp_profile_dir):
            if this_file.endswith(".json") and this_file not in  ('pp_showlist.json','schedule.json'):
                self.mon.log (self,"Updating medialist " + this_file)
                # open a medialist and update its tracks
                ifile  = open(self.pp_profile_dir + os.sep + this_file, 'rb')
                tracks = json.load(ifile)['tracks']
                ifile.close()
                replacement_tracks=self.update_tracks(tracks)
                dic={'issue':self.editor_issue,'tracks':replacement_tracks}
                ofile  = open(self.pp_profile_dir + os.sep + this_file, "wb")
                json.dump(dic,ofile,sort_keys=True,indent=1)       



    def update_tracks(self,old_tracks):
        # get correct spec from type of field
        replacement_tracks=[]
        for old_track in old_tracks:
            # print '\nold track ',old_track
            track_type=old_track['type']
            #update if new tracks has the track type otherwise skip
            if track_type in PPdefinitions.new_tracks:
                spec_fields=PPdefinitions.new_tracks[track_type]
                left_overs=dict()
                # go through track and delete fields not in spec
                for key in old_track.keys():
                    if key in spec_fields:
                        left_overs[key]=old_track[key]
                # print '\n leftovers',left_overs
                replacement_track=copy.deepcopy(PPdefinitions.new_tracks[track_type])
                # print '\n before update', replacement_track
                replacement_track.update(left_overs)
                # print '\nafter update',replacement_track
                replacement_tracks.append(copy.deepcopy(replacement_track))
        return replacement_tracks


    def update_shows_in_showlist(self,old_shows):
        # get correct spec from type of field
        replacement_shows=[]
        for old_show in old_shows:
            show_type=old_show['type']
            ## menu to menushow
            spec_fields=PPdefinitions.new_shows[show_type]
            left_overs=dict()
            # go through track and delete fields not in spec
            for key in old_show.keys():
                if key in spec_fields:
                    left_overs[key]=old_show[key]
            # print '\n leftovers',left_overs
            replacement_show=copy.deepcopy(PPdefinitions.new_shows[show_type])
            replacement_show.update(left_overs)
            replacement_shows.append(copy.deepcopy(replacement_show))
        return replacement_shows                
Пример #7
0
class OSCDriver(object):

    # executed by main program
    def  init(self,pp_profile,manager_unit,preferred_interface,my_ip,show_command_callback,input_event_callback,animate_callback):

        self.pp_profile=pp_profile
        self.show_command_callback=show_command_callback
        self.input_event_callback=input_event_callback
        self.animate_callback=animate_callback

        self.mon=Monitor()
        config_file=self.pp_profile + os.sep +'pp_io_config'+os.sep+ 'osc.cfg'
        if not os.path.exists(config_file):
            self.mon.err(self, 'OSC Configuration file not found: '+config_file)
            return'error','OSC Configuration file nof found: '+config_file
        
        self.mon.log(self, 'OSC Configuration file found at: '+config_file)        
        self.osc_config=OSCConfig()
        
        # reads config data 
        if self.osc_config.read(config_file) ==False:
            return 'error','failed to read osc.cfg'

        # unpack config data and initialise

        if self.osc_config.this_unit_name =='':
            return 'error','OSC Config -  This Unit has no name'
        if len(self.osc_config.this_unit_name.split())>1:
            return 'error','OSC config - This Unit Name not a single word: '+self.osc_config.this_unit_name
        self.this_unit_name=self.osc_config.this_unit_name
               
                 
        if self.osc_config.this_unit_ip=='':
            self.this_unit_ip=my_ip
        else:
            self.this_unit_ip=self.osc_config.this_unit_ip

        if self.osc_config.slave_enabled == 'yes':
            if not self.osc_config.listen_port.isdigit():
                return 'error','OSC Config - Listen port is not a positve number: '+ self.osc_config.listen_port
            self.listen_port= self.osc_config.listen_port

        if self.osc_config.master_enabled == 'yes':
            if not self.osc_config.reply_listen_port.isdigit():
                return 'error','OSC Config - Reply Listen port is not a positve number: '+ self.osc_config.reply_listen_port
            self.reply_listen_port= self.osc_config.reply_listen_port

            # prepare the list of slaves
            status,message=self.parse_slaves()
            if status=='error':
                return status,message
             

        self.prefix='/pipresents'
        self.this_unit='/' + self.this_unit_name
        
        self.input_server=None
        self.input_reply_client=None
        self.input_st=None
        
        self.output_client=None
        self.output_reply_server=None
        self.output_reply_st=None


        if self.osc_config.slave_enabled == 'yes' and self.osc_config.master_enabled == 'yes' and self.listen_port == self.reply_listen_port:
            # The two listen ports are the same so use one server for input and output

            #start the client that sends commands to the slaves
            self.output_client=OSC.OSCClient()
            self.mon.log(self, 'sending commands to slaves and replies to master on: '+self.reply_listen_port)

            #start the input+output reply server
            self.mon.log(self, 'listen to commands and replies from slave units using: ' + self.this_unit_ip+':'+self.reply_listen_port)
            self.output_reply_server=myOSCServer((self.this_unit_ip,int(self.reply_listen_port)),self.output_client)
            self.add_default_handler(self.output_reply_server)
            self.add_input_handlers(self.output_reply_server)
            self.add_output_reply_handlers(self.output_reply_server)

            self.input_server=self.output_reply_server
            
        else:

            if self.osc_config.slave_enabled == 'yes':
                # we want this to be a slave to something else

                # start the client that sends replies to controlling unit
                self.input_reply_client=OSC.OSCClient()
                
                #start the input server
                self.mon.log(self, 'listening to commands on: ' + self.this_unit_ip+':'+self.listen_port)
                self.input_server=myOSCServer((self.this_unit_ip,int(self.listen_port)),self.input_reply_client)
                self.add_default_handler(self.input_server)
                self.add_input_handlers(self.input_server)
                print self.pretty_list(self.input_server.getOSCAddressSpace(),'\n')
                
            if self.osc_config.master_enabled =='yes':
                #we want to control other units
               
                #start the client that sends commands to the slaves
                self.output_client=OSC.OSCClient()
                self.mon.log(self, 'sending commands to slaves on port: '+self.reply_listen_port)

                #start the output reply server
                self.mon.log(self, 'listen to replies from slave units using: ' + self.this_unit_ip+':'+self.reply_listen_port)
                self.output_reply_server=myOSCServer((self.this_unit_ip,int(self.reply_listen_port)),self.output_client)
                self.add_default_handler(self.output_reply_server)
                self.add_output_reply_handlers(self.output_reply_server)
        
        return 'normal','osc.cfg read'


    def terminate(self):
        if self.input_server != None:
            self.input_server.close()
        if self.output_reply_server != None:
            self.output_reply_server.close()
        self.mon.log(self, 'Waiting for Server threads to finish')
        if self.input_st != None:
            self.input_st.join() ##!!!
        if self.output_reply_st != None:
            self.output_reply_st.join() ##!!!
        self.mon.log(self,'server threads closed')
        if self.input_reply_client !=None:
            self.input_reply_client.close()
        if self.output_client !=None:
            self.output_client.close()


    def start_server(self):
        # Start input Server
        self.mon.log(self,'Starting input OSCServer')
        if self.input_server != None:
            self.input_st = threading.Thread( target = self.input_server.serve_forever )
            self.input_st.start()

        # Start output_reply server
        self.mon.log(self,'Starting output reply OSCServer')
        if self.output_reply_server != None:
            self.output_reply_st = threading.Thread( target = self.output_reply_server.serve_forever )
            self.output_reply_st.start()

    def parse_slaves(self):
        name_list=self.osc_config.slave_units_name.split()
        ip_list=self.osc_config.slave_units_ip.split()
        if len(name_list)==0:
            return 'error','OSC Config - List of slaves name is empty'
        if len(name_list) != len(ip_list):
            return 'error','OSC Config - Lengths of list of slaves name and slaves IP is different'       
        self.slave_name_list=[]
        self.slave_ip_list=[]
        for i, name in enumerate(name_list):
            self.slave_name_list.append(name)
            self.slave_ip_list.append(ip_list[i])
        return 'normal','slaves parsed'


    def parse_osc_command(self,fields):
    # send message to slave unit - INTERFACE WITH pipresents
        if len(fields) <2:
               return 'error','too few fields in OSC command '+' '.join(fields)
        to_unit_name=fields[0]
        show_command=fields[1]
        # print 'FIELDS ',fields
        
        # send an arbitary osc message            
        if show_command == 'send':
            if len(fields)>2:
                osc_address= fields[2]
                arg_list=[]
                if len(fields)>3:
                    arg_list=fields[3:]
            else:
                return 'error','OSC - wrong nmber of fields in '+ ' '.join(fields)

        elif show_command in ('open','close','openexclusive'):
            if len(fields)==3:
                osc_address=self.prefix+'/'+ to_unit_name + '/core/'+ show_command
                arg_list= [fields[2]]
            else:
                return 'error','OSC - wrong number of fields in '+ ' '.join(fields)
                
        elif show_command =='monitor':
            if fields[2] in ('on','off'):
                osc_address=self.prefix+'/'+ to_unit_name + '/core/'+ show_command
                arg_list=[fields[2]]
            else:
                self.mon.err(self,'OSC - illegal state in '+ show_command + ' '+fields[2])
        
        elif show_command =='event':                
            if len(fields)==3:
                osc_address=self.prefix+'/'+ to_unit_name + '/core/'+ show_command
                arg_list= [fields[2]]


        elif show_command == 'animate':                
            if len(fields)>2:
                osc_address=self.prefix+'/'+ to_unit_name + '/core/'+ show_command
                arg_list= fields[2:]
            else:
                return 'error','OSC - wrong nmber of fields in '+ ' '.join(fields)
            
        elif show_command in ('closeall','exitpipresents','shutdownnow','reboot'):
            if len(fields)==2:
                osc_address=self.prefix+'/'+ to_unit_name + '/core/'+ show_command
                arg_list= []
            else:
                return 'error','OSC - wrong nmber of fields in '+ ' '.join(fields)

        elif show_command in ('loopback','server-info'):
            if len(fields)==2:
                osc_address=self.prefix+'/'+ to_unit_name + '/system/'+ show_command
                arg_list= []
            else:
                return 'error','OSC - wrong nmber of fields in '+ ' '.join(fields)
            
        else:
            return 'error','OSC - unkown command in '+ ' '.join(fields)

        
        ip=self.find_ip(to_unit_name,self.slave_name_list,self.slave_ip_list)
        if ip=='':
            return 'warn','OSC Unit Name not in the list of slaves: '+ to_unit_name
        self.sendto(ip,osc_address,arg_list)
        return 'normal','osc command sent'


    def find_ip(self,name,name_list,ip_list):
        i=0
        for j in name_list:
            if j == name:
                break
            i=i+1
            
        if i==len(name_list):
            return ''
        else:
            return ip_list[i]
                    

    def sendto(self,ip,osc_address,arg_list):
        # print ip,osc_address,arg_list
        if self.output_client is None:
            self.mon.warn(self,'Master not enabled, ignoring OSC command')
            return
        msg = OSC.OSCMessage()
        # print address
        msg.setAddress(osc_address)
        for arg in arg_list:
            # print arg
            msg.append(arg)
            
        try:
            self.output_client.sendto(msg,(ip,int(self.reply_listen_port)))
            self.mon.log(self,'Sent OSC command: '+osc_address+' '+' '.join(arg_list) + ' to '+ ip +':'+self.reply_listen_port)
        except Exception as e:
            self.mon.warn(self,'error in client when sending OSC command: '+ str(e))


# **************************************
# Handlers for fallback
# **************************************

    def add_default_handler(self,server):
        server.addMsgHandler('default', self.no_match_handler)

    def no_match_handler(self,addr, tags, stuff, source):
        text= "No handler for message from %s" % OSC.getUrlStr(source)+'\n'
        text+= "     %s" % addr+ self.pretty_list(stuff,'')
        self.mon.warn(self,text)
        return None

# **************************************
# Handlers for Slave (input)
# **************************************    

    def add_input_handlers(self,server):
        server.addMsgHandler(self.prefix + self.this_unit+"/system/server-info", self.server_info_handler)
        server.addMsgHandler(self.prefix + self.this_unit+"/system/loopback", self.loopback_handler)
        server.addMsgHandler(self.prefix+ self.this_unit+'/core/open', self.open_show_handler)
        server.addMsgHandler(self.prefix+ self.this_unit+'/core/close', self.close_show_handler)
        server.addMsgHandler(self.prefix+ self.this_unit+'/core/openexclusive', self.openexclusive_handler)
        server.addMsgHandler(self.prefix+ self.this_unit+'/core/closeall', self.closeall_handler)
        server.addMsgHandler(self.prefix+ self.this_unit+'/core/exitpipresents', self.exitpipresents_handler)
        server.addMsgHandler(self.prefix+ self.this_unit+'/core/shutdownnow', self.shutdownnow_handler)
        server.addMsgHandler(self.prefix+ self.this_unit+'/core/reboot', self.reboot_handler)
        server.addMsgHandler(self.prefix+ self.this_unit+'/core/event', self.input_event_handler)
        server.addMsgHandler(self.prefix+ self.this_unit+'/core/animate', self.animate_handler)
        server.addMsgHandler(self.prefix+ self.this_unit+'/core/monitor', self.monitor_handler)


    # reply to master unit with name of this unit and commands
    def server_info_handler(self,addr, tags, stuff, source):

        msg = OSC.OSCMessage(self.prefix+'/system/server-info-reply')
        msg.append(self.this_unit_name)
        msg.append(self.input_server.getOSCAddressSpace())
        self.mon.log(self,'Sent Server Info reply to %s:' % OSC.getUrlStr(source))
        return msg


    # reply to master unit with a loopback message
    def loopback_handler(self,addr, tags, stuff, source):
        msg = OSC.OSCMessage(self.prefix+'/system/loopback-reply')
        self.mon.log(self,'Sent loopback reply to %s:' % OSC.getUrlStr(source))
        return msg


 
    def open_show_handler(self,address, tags, args, source):
        self.prepare_show_command_callback('open ',args,1)

    def openexclusive_handler(self,address, tags, args, source):
        self.prepare_show_command_callback('openexclusive ',args,1)
        
    def close_show_handler(self,address, tags, args, source):
        self.prepare_show_command_callback('close ', args,1)

    def closeall_handler(self,address, tags, args, source):
        self.prepare_show_command_callback('closeall',args,0)

    def monitor_handler(self,address, tags, args, source):
        self.prepare_show_command_callback('monitor ', args,1)

    def exitpipresents_handler(self,address, tags, args, source):
        self.prepare_show_command_callback('exitpipresents',args,0)

    def reboot_handler(self,address, tags, args, source):
        self.prepare_show_command_callback('reboot',args,0)

    def shutdownnow_handler(self,address, tags, args, source):
        self.prepare_show_command_callback('shutdownnow',args,0)

    def prepare_show_command_callback(self,command,args,limit):
        if len(args) == limit:
            if limit !=0:
                self.mon.sched(self,TimeOfDay.now,'Received from OSC: '+ command + ' ' +args[0])
                self.show_command_callback(command+args[0])
            else:
                self.mon.sched(self,TimeOfDay.now,'Received from OSC: '+ command)
                self.show_command_callback(command)                
        else:
            self.mon.warn(self,'OSC show command does not have '+limit +' argument - ignoring')  

    def input_event_handler(self,address, tags, args, source):
        if len(args) == 1:
            self.input_event_callback(args[0],'OSC')
        else:
            self.mon.warn(self,'OSC input event does not have 1 argument - ignoring')    


    def animate_handler(self,address, tags, args, source):
        if len(args) !=0:
            # delay symbol,param_type,param_values,req_time as a string
            text='0 '
            for arg in args:
                text= text+ arg + ' '
            text = text + '0'
            print text
            self.animate_callback(text)
        else:
            self.mon.warn(self,'OSC output event has no arguments - ignoring')      

# **************************************
# Handlers for Master- replies from slaves (output)
# **************************************

    # reply handlers do not have the destinatuion unit in the address as they are always sent to the originator
    def add_output_reply_handlers(self,server):
        server.addMsgHandler(self.prefix+"/system/server-info-reply", self.server_info_reply_handler)
        server.addMsgHandler(self.prefix+"/system/loopback-reply", self.loopback_reply_handler)
        
        
    # print result of info request from slave unit
    def server_info_reply_handler(self,addr, tags, stuff, source):
        self.mon.log(self,'server info reply from slave '+OSC.getUrlStr(source)+ self.pretty_list(stuff,'\n'))
        print 'Received reply to Server-Info command from slave: ',OSC.getUrlStr(source), self.pretty_list(stuff,'\n')
        return None

    #print result of info request from slave unit
    def loopback_reply_handler(self,addr, tags, stuff, source):
        self.mon.log(self,'server info reply from slave '+OSC.getUrlStr(source)+ self.pretty_list(stuff,'\n'))
        print 'Received reply to Loopback command from slave: ' + OSC.getUrlStr(source)+ ' '+ self.pretty_list(stuff,'\n')
        return None


    def pretty_list(self,fields, separator):
        text=' '
        for field in fields:
            text += str(field) + separator
        return text+'\n'
Пример #8
0
class OSCRemote(object):

    def __init__(self):
    
        self.editor_issue="1.3"

        # get command options
        self.command_options=remote_options()

        # get directory holding the code
        self.pp_dir=sys.path[0]
            
        if not os.path.exists(self.pp_dir+os.sep+"pp_oscremote.py"):
            tkMessageBox.showwarning("Pi Presents","Bad Application Directory")
            exit()
            
          
        # Initialise logging
        Monitor.log_path=self.pp_dir
        self.mon=Monitor()
        self.mon.init()
        
        Monitor.classes  = ['OSCRemote','OSCConfig','OSCEditor']

        Monitor.log_level = int(self.command_options['debug'])

        self.mon.log (self, "Pi Presents Remote is starting")
        self.mon.log (self," OS and separator " + os.name +'  ' + os.sep)
        self.mon.log(self,"sys.path[0] -  location of code: code "+sys.path[0])

        self.setup_gui()

        # OSC config class
        self.osc_config=OSCConfig()

        self.init()

        #and start the system
        self.root.after(1000,self.run_app)
        self.root.mainloop()


    def init(self):
        self.osc_config_file = self.pp_dir + os.sep + 'pp_config' + os.sep + 'pp_oscremote.cfg'
        self.read_create_osc()
        self.pp_home_dir = self.osc_config.pp_home_dir
        self.pp_profiles_offset = self.osc_config.pp_profiles_offset
        self.mon.log(self,"Data Home from options is "+self.pp_home_dir)
        self.mon.log(self,"Current Profiles Offset from options is "+self.pp_profiles_offset)
        self.pp_profile_dir=''

        self.current_showlist=None
        self.current_show=None
        self.current_show_ref=''
        self.shows_display.delete(0,END)
        self.results.set('')

        
    def add_status(self,text):
        self.status_display.insert(END,text+'\n')
        self.status_display.see(END)

    def run_app(self):
        self.client=None
        self.server=None
        self.st=None
        
        # initialise OSC variables
        self.prefix='/pipresents'
        self.this_unit='/' + self.osc_config.this_unit_name
        self.add_status('this unit is: '+self.this_unit)
        self.controlled_unit='/'+self.osc_config.controlled_unit_1_name
        self.add_status('controlled unit is: '+self.controlled_unit)
       
        #connect client then start server to listen for replies
        self.init_client()
        self.add_status('connecting to controlled unit: '+self.osc_config.controlled_unit_1_ip+':'+self.osc_config.controlled_unit_1_port +' '+self.osc_config.controlled_unit_1_name)
        self.connect_client(self.osc_config.controlled_unit_1_ip,self.osc_config.controlled_unit_1_port)
        self.add_status('listening for replies on:'+self.osc_config.this_unit_ip+':'+self.osc_config.this_unit_port)
        self.init_server(self.osc_config.this_unit_ip,self.osc_config.this_unit_port,self.client)
        self.add_initial_handlers()
        self.start_server()


# ***************************************
#  RESPOND TO BUTTONS
# ***************************************

    def open_show(self):
        self.msg_path= '/core/open '
        self.msg_arg_text= self.current_show_ref
        self.display_msg_text()


    def close_show(self):
        self.msg_path= '/core/close '
        self.msg_arg_text=self.current_show_ref
        self.display_msg_text()


    def exit_pipresents(self):
        self.msg_path= '/core/exitpipresents'
        self.msg_arg_text=''
        self.display_msg_text()

    
    def play_event(self):
        self.msg_path= '/core/event'
        self.msg_arg_text = 'pp-play'
        self.display_msg_text()


    def pause_event(self):
        self.msg_path= '/core/event'
        self.msg_arg_text='pp-pause'
        self.display_msg_text()
        pass

    def stop_event(self):
        self.msg_path= '/core/event'
        self.msg_arg_text='pp-stop'
        self.display_msg_text()
        pass
    
    def up_event(self):
        self.msg_path= '/core/event'
        self.msg_arg_text='pp-up'
        self.display_msg_text()


    def down_event(self):
        self.msg_path= '/core/event'
        self.msg_arg_text='pp-down'
        self.display_msg_text()


    def output(self):
        self.msg_path= '/core/output'
        self.msg_arg_text=''
        self.display_msg_text()

    def loopback(self):
        self.msg_path= '/system/loopback'
        self.msg_arg_text=''
        self.display_msg_text()        


    def server_info(self):
        self.msg_path= '/system/server-info'
        self.msg_arg_text=''
        self.display_msg_text()         


    # and put the created text in the results box in the gui
    def display_msg_text(self):
        self.results.set(self.prefix+self.controlled_unit+self.msg_path+' '+self.msg_arg_text)


    #calback from the Send button
    # parses the message string into fields and sends - NO error checking
    def send_message(self):
        msg_text=self.results.get()
        self.add_status('Send message:'+msg_text)
        self.mon.log(self,'send message: ' + msg_text )
        fields=msg_text.split()
        address = fields[0]
        arg_list=fields[1:]
        self.send(address,arg_list)


    # ***************************************
    # OSC CLIENT TO SEND MESSAGES
    # ***************************************

    def init_client(self):
       self.client = OSC.OSCClient()

    def connect_client(self,ip,port):
        self.mon.log(self,'connect to: '+ip+':'+str(port))
        self.client.connect( (ip, int(port)) ) 


    def send(self,address,arg_list):
        msg = OSC.OSCMessage()
        msg.setAddress(address)
        for arg in arg_list:
            msg.append(arg)
        self.client.send(msg)    


    def disconnect_client(self):
        self.client.close()
        return


    # ***************************************
    # OSC SERVER TO LISTEN TO REPLIES
    # ***************************************

    def init_server(self,ip,port_text,client):
        self.mon.log(self,'Start Server: '+ip+':'+port_text)
        self.server = OSC.OSCServer((ip,int(port_text)),client)

    def start_server(self):
        self.st = threading.Thread( target = self.server.serve_forever )
        self.st.start()

    def close_server(self):
        if self.server != None:
            self.server.close()
        self.mon.log(self, 'Waiting for Server-thread to finish')
        if self.st != None:
            self.st.join() ##!!!
        self.mon.log(self,'server thread closed')


    def add_initial_handlers(self):
        self.server.addMsgHandler('default', self.no_match_handler)     
        self.server.addMsgHandler(self.prefix+self.this_unit+"/system/loopback-reply", self.loopback_reply_handler)
        self.server.addMsgHandler(self.prefix+self.this_unit+"/system/server-info-reply", self.server_info_reply_handler)

    def no_match_handler(self,addr, tags, stuff, source):
        text=''
        text+= "no match for new osc msg from %s" % OSC.getUrlStr(source)+'\n'
        text+= "with addr : %s" % addr+'\n'
        text+= "typetags %s" % tags+'\n'
        text+= "data %s" % stuff+'\n'
        self.add_status(text+'\n')

        
    def loopback_reply_handler(self,addr, tags, stuff, source):
        self.add_status('Loopback reply  received from: '+  self.pretty_list(source))


    def server_info_reply_handler(self,addr, tags, stuff, source):
        self.add_status('Server Information from: '+  self.pretty_list(source)  + '\n   ' + self.pretty_list(stuff))


    def pretty_list(self,fields):
        text=' '
        for field in fields:
            text += str(field) + ' '
        return text


    # ***************************************
    # INIT EXIT MISC
    # ***************************************

    def e_edit_osc(self):
        self.disconnect_client()
        self.close_server()
        self.edit_osc()
        self.init()
        self.add_status('\n\n\nRESTART')
        self.run_app()


    def app_exit(self):
        self.disconnect_client()
        self.close_server()
        if self.root is not None:
            self.root.destroy()
        self.mon.finish()
        sys.exit() 


    def show_help (self):
        tkMessageBox.showinfo("Help","Read 'manual.pdf'")
  

    def about (self):
        tkMessageBox.showinfo("About","Simple Remote Control for Pi Presents\n"
                              +"Author: Ken Thompson"
                              +"\nWebsite: http://pipresents.wordpress.com/")


    def setup_gui(self):
        # set up the gui
 
        # root is the Tkinter root widget
        self.root = Tk()
        self.root.title("Remote Control for Pi Presents")

        # self.root.configure(background='grey')

        self.root.resizable(False,False)

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

        # bind some display fields
        self.filename = StringVar()
        self.display_show = StringVar()
        self.results = StringVar()
        self.status = StringVar()

        # define menu
        menubar = Menu(self.root)

        profilemenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        profilemenu.add_command(label='Select', command = self.open_existing_profile)
        menubar.add_cascade(label='Profile', menu = profilemenu)

        
        toolsmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='Tools', menu = toolsmenu)

        osc_configmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='OSC', menu = osc_configmenu)
        osc_configmenu.add_command(label='Edit', command = self.e_edit_osc)

        helpmenu = Menu(menubar, tearoff=0, bg="grey", fg="black")
        menubar.add_cascade(label='Help', menu = helpmenu)
        helpmenu.add_command(label='Help', command = self.show_help)
        helpmenu.add_command(label='About', command = self.about)
         
        self.root.config(menu=menubar)



        #top frame
        top_frame=Frame(self.root,padx=5,pady=5)
        top_frame.pack(side=TOP)
        
        results_label = Label(top_frame, text="Message to Send",font="arial 12 bold")
        results_label.pack(side=LEFT)
        results_display=Entry(top_frame, textvariable=self.results, width=70)
        results_display.pack(side=LEFT,fill=BOTH, expand=1)
        send_button = Button(top_frame, width = 5, height = 1, text='Send',
                            fg='black', command = self.send_message, bg="light grey")
        send_button.pack(side=RIGHT)

        #bottom frame
        bottom_frame=Frame(self.root,padx=5,pady=5)
        bottom_frame.pack(side=TOP, fill=BOTH, expand=1) 
        left_frame=Frame(bottom_frame, padx=5)
        left_frame.pack(side=LEFT)            
        right_frame=Frame(bottom_frame,padx=5,pady=5)
        right_frame.pack(side=LEFT)

        suplabel_frame=Frame(right_frame,pady=5)
        suplabel_frame.pack(side=TOP)
        commands_label = Label(suplabel_frame, text="Show Control",font="arial 12 bold")
        commands_label.pack()
                            
        supervisor_frame=Frame(right_frame,pady=5)
        supervisor_frame.pack(side=TOP)

        # supervisor buttons 
        add_button = Button(supervisor_frame, width = 5, height = 1, text='Open\nShow',
                            fg='black', command = self.open_show, bg="light grey")
        add_button.pack(side=LEFT)
        add_button = Button(supervisor_frame, width = 5, height = 1, text='Close\nShow',
                            fg='black', command = self.close_show, bg="light grey")
        add_button.pack(side=LEFT)
        add_button = Button(supervisor_frame, width = 10, height = 1, text='Exit\nPi Presents',
                            fg='black', command = self.exit_pipresents, bg="light grey")
        add_button.pack(side=LEFT)

        # events buttons        
        oplabel_frame=Frame(right_frame,pady=5)
        oplabel_frame.pack(side=TOP)
        operations_label = Label(oplabel_frame, text="Input Events",
                                 font="arial 12 bold")
        operations_label.pack()
        
        operations_frame=Frame(right_frame,pady=5)
        operations_frame.pack(side=TOP)
                              
        add_button = Button(operations_frame, width = 5, height = 1, text='Play',
                            fg='black', command = self.play_event, bg="light grey")
        add_button.pack(side=LEFT)   
        add_button = Button(operations_frame, width = 5, height = 1, text='Pause',
                            fg='black', command = self.pause_event, bg="light grey")
        add_button.pack(side=LEFT)
        add_button = Button(operations_frame, width = 5, height = 1, text='Stop',
                            fg='black', command = self.stop_event, bg="light grey")
        add_button.pack(side=LEFT)
        add_button = Button(operations_frame, width = 5, height = 1, text='Up',
                            fg='black', command = self.up_event, bg="light grey")
        add_button.pack(side=LEFT)
        add_button = Button(operations_frame, width = 5, height = 1, text='Down',
                            fg='black', command = self.down_event, bg="light grey")
        add_button.pack(side=LEFT)


        # animate buttons        
        animate_frame=Frame(right_frame,pady=5)
        animate_frame.pack(side=TOP)
        animate_label = Label(animate_frame, text="Control Outputs",
                                 font="arial 12 bold")
        animate_label.pack()
        
        animate_frame=Frame(right_frame,pady=5)
        animate_frame.pack(side=TOP)
                              
        add_button = Button(animate_frame, width = 5, height = 1, text='Output',
                            fg='black', command = self.output, bg="light grey")
        add_button.pack(side=LEFT)  

        
        # system buttons        
        systemlabel_frame=Frame(right_frame,pady=5)
        systemlabel_frame.pack(side=TOP)
        system_label = Label(systemlabel_frame, text="System",
                                 font="arial 12 bold")
        system_label.pack()
        
        system_frame=Frame(right_frame,pady=5)
        system_frame.pack(side=TOP)
                              
        add_button = Button(system_frame, width = 5, height = 1, text='Loopback',
                            fg='black', command = self.loopback, bg="light grey")
        add_button.pack(side=LEFT)   
        add_button = Button(system_frame, width = 10, height = 1, text='Server Info',
                            fg='black', command = self.server_info, bg="light grey")
        add_button.pack(side=LEFT)



        # define display of showlist
        shows_title_frame=Frame(left_frame)
        shows_title_frame.pack(side=TOP)
        shows_label = Label(shows_title_frame, text="Shows")
        shows_label.pack()
        shows_frame=Frame(left_frame)
        shows_frame.pack(side=TOP)
        scrollbar = Scrollbar(shows_frame, orient=VERTICAL)
        self.shows_display = Listbox(shows_frame, selectmode=SINGLE, height=12,
                                     width = 40, bg="white",activestyle=NONE,
                                     fg="black", yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.shows_display.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.shows_display.pack(side=LEFT, fill=BOTH, expand=1)
        self.shows_display.bind("<ButtonRelease-1>", self.e_select_show)


        # status_frame      
        status_frame=Frame(self.root,padx=5,pady=5)
        status_frame.pack(side=TOP, fill=BOTH, expand=1)
        status_label = Label(status_frame, text="Status",font="arial 12 bold")
        status_label.pack(side=LEFT)
        scrollbar = Scrollbar(status_frame, orient=VERTICAL)
        self.status_display=Text(status_frame,height=10, yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.status_display.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.status_display.pack(side=LEFT,fill=BOTH, expand=1)


    # ***************************************
    # SHOWLIST
    # ***************************************

    def open_existing_profile(self):
        initial_dir=self.pp_home_dir+os.sep+"pp_profiles"+self.pp_profiles_offset
        if os.path.exists(initial_dir) is False:
            self.mon.err(self,"Profiles directory not found: " + initial_dir + "\n\nHint: Data Home option must end in pp_home")
            return
        dir_path=tkFileDialog.askdirectory(initialdir=initial_dir)
        # dir_path="C:\Users\Ken\pp_home\pp_profiles\\ttt"
        if len(dir_path)>0:
            self.open_profile(dir_path)

    def open_profile(self,dir_path):
        showlist_file = dir_path + os.sep + "pp_showlist.json"
        if os.path.exists(showlist_file) is False:
            self.mon.err(self,"Not a Profile: " + dir_path + "\n\nHint: Have you opened the profile directory?")
            return
        self.pp_profile_dir = dir_path
        self.root.title("Remote for Pi Presents - "+ self.pp_profile_dir)
        self.open_showlist(self.pp_profile_dir)

    def open_showlist(self,profile_dir):
        showlist_file = profile_dir + os.sep + "pp_showlist.json"
        if os.path.exists(showlist_file) is False:
            self.mon.err(self,"showlist file not found at " + profile_dir + "\n\nHint: Have you opened the profile directory?")
            self.app_exit()
        self.current_showlist=ShowList()
        self.current_showlist.open_json(showlist_file)
        if float(self.current_showlist.sissue()) != float(self.editor_issue):
            self.mon.err(self,"Version of profile does not match Remote: "+self.editor_issue)
            self.app_exit()
        self.refresh_shows_display()


    def refresh_shows_display(self):
        self.shows_display.delete(0,self.shows_display.size())
        for index in range(self.current_showlist.length()):
            self.shows_display.insert(END, self.current_showlist.show(index)['title']+"   ["+self.current_showlist.show(index)['show-ref']+"]")        
        if self.current_showlist.show_is_selected():
            self.shows_display.itemconfig(self.current_showlist.selected_show_index(),fg='red')            
            self.shows_display.see(self.current_showlist.selected_show_index())

    def e_select_show(self,event):
        print 'select show', self.current_showlist.length()
        if self.current_showlist is not None and self.current_showlist.length()>0:
            mouse_item_index=int(event.widget.curselection()[0])
            self.current_showlist.select(mouse_item_index)
            self.current_show_ref=self.current_showlist.selected_show()['show-ref']
            self.refresh_shows_display()
        else:
            self.current_show_ref=''


# ***************************************
#  OSC CONFIGURATION
# ***************************************

    def read_create_osc(self):
        if self.osc_config.read(self.osc_config_file) is False:
            self.osc_config.create(self.osc_config_file)
            eosc = OSCEditor(self.root, self.osc_config_file,'remote','Create OSC Remote Configuration')
            self.osc_config.read(self.osc_config_file)


    def edit_osc(self):
        if self.osc_config.read(self.osc_config_file) is False:
            self.osc_config.create(self.osc_config_file)
        eosc = OSCEditor(self.root, self.osc_config_file,'remote','Edit OSC Reomote Configuration')