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')
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)
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'
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')
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
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'
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')