#!/usr/bin/env python ''' transmogrifier.py: beta version dependencies: Python2.5, wxPython, pyserial2.3+, simpleosc0.2.5, ftdi driver authors: Matt Nelson, Robb Drinkwater, Ed Bennett last edited: Thu Aug 1 13:30:22 2008 http://artbus.info CHANGE LOG: 1.1 Beta - Removed || USB from MainFrame getDevices method, not needed because of -i & will throw an error on Linux (rd) 1.2 Beta - Changed the SocketThread inbound method to build an osc ab message with spaces (mn) - Removed raise from Transmogrify try except block (mn) 1.3 Beta - Changed the SocketThread inbound method to be more pythonic (rd, mn) - Corrected spelling error (mn) 1.4 Beta - Changed name to just transmogrifier.py. (mn) 1.5 Beta - Changed getDevices method, it now checks if ports can be opened prior to adding them to dev_lt. (mn) ''' __version__ = '1.5 Beta' import wx, wx.html, string, os, sys, serial, Queue, time, threading, socket, osc class WorkerThread(threading.Thread): '''This class creates a tread that sets up the sockets and waits for a connection to be made to the server. It then does a callback to main gui thread to start the other threads.''' def __init__(self, threadNum, window, host, port, baud, dev, protocol): threading.Thread.__init__(self) self.threadNum = threadNum self.window = window self.host = host self.port = port self.baud = baud self.dev = dev self.protocol = protocol self.clientsock = 0 def stop(self): '''Called when thread stops. Closes socket and calls the ugly hack when thread is stopped.''' if self.clientsock == 0: self.uglyHack() # TCP if self.protocol == 'tcp': self.sock.close() def run(self): '''Called when thread runs. Sets up socket and serial ports, then does a callback to the gui thread.''' #print 'workerThread: Started', currentThread().getName() # Statusbar update msg = 'Status: Running' wx.CallAfter(self.window.updateStatus, msg) # Check protocol and setup socket if self.protocol == 'tcp': self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind((self.host, self.port)) self.sock.listen(1) if self.protocol == 'osc': osc.init() self.clientsock = osc.createListener(self.host, self.port) self.clientsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Serial setup dataQueue = Queue.Queue(128) ser = serial.Serial() ser.port = self.dev ser.baudrate = self.baud ser.rtscts = False ser.xonxoff = False ser.timeout = 1 # Callback to update statusbar and gauge msg = dataQueue.qsize() # Return queue size wx.CallAfter(self.window.updateQueue, msg) # Callback to gui thread to update statusbar and gauge # Check protocol and set socket if self.protocol == 'tcp': try: self.clientsock, clientaddr = self.sock.accept() except KeyboardInterrupt: raise except: #traceback.print_exc() pass try: ser.open() except KeyboardInterrupt: raise except: #traceback.print_exc() pass # Callback to gui thread wx.CallAfter(self.window.setThreads, self.clientsock, ser, dataQueue) def uglyHack(self): '''This is an ugly hack to allow the socket to close cleanly. It connects to the socket and sends foo data and closes the port. I just swallowed a small amount of barf.''' hackData = 'nill' hackOut = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: hackOut.connect((self.host, self.port)) hackOut.send(hackData) finally: hackOut.close() class SocketThread(threading.Thread): '''This class creates a tread that manages the data coming into the server. It then puts it in a queue.''' def __init__(self, threadNum, window, clientsock, dataQueue, protocol, message='nill'): threading.Thread.__init__(self) self.threadNum = threadNum self.window = window self.clientsock = clientsock self.dataQueue = dataQueue self.protocol = protocol self.message = message self.abortFlag = 0 def stop(self): '''Called when thread stops. Closes socket and calls the ugly hack when thread is stopped.''' self.abortFlag = 1 self.clientsock.close() def inbound(self, *msg): '''Strips data from osc message and puts in queue''' # All data to end of message osc_msg = msg[0][2:] data = '' # This will build the osc message if it contains spaces for d in osc_msg: if not str(d).startswith("!"): # don't add a leading space to the command data += " " data += str(d) self.dataQueue.put(data) # Put data into the queue def run(self): '''Called when thread runs. Waits for connection, then waits for data, then puts the data in the queue.''' #print 'handleTCP: Started', currentThread().getName() #print 'Got connection from', self.clientsock.getpeername() # OSC if self.protocol == 'osc': osc.bind(self.inbound, self.message) while(self.abortFlag == 0): # TCP if self.protocol == 'tcp': data = self.clientsock.recv(4096) # Blocking until client connection self.dataQueue.put(data) # Put data into the queue # OSC if self.protocol == 'osc': osc.getOSC(self.clientsock) time.sleep(0.0001) # Sleep to prevent thread come consuming cpu cycles class SerThread(threading.Thread): '''This class creates a tread that waits for data to be put into the queue to be set. It then pops data of the queue and sends it through the serial port. If the command is one that returns data it then reads the data from the serial port.''' def __init__(self, threadNum, window, clientsock, ser, dataQueue, protocol, oscHost='nill', oscPort=0): threading.Thread.__init__(self) self.threadNum = threadNum self.window = window self.clientsock = clientsock self.ser = ser self.dataQueue = dataQueue self.protocol = protocol self.oscHost = oscHost self.oscPort = oscPort self.abortFlag = 0 def stop(self): '''Called when thread stops. Sets the abort flag.''' self.abortFlag = 1 def run(self): '''Called when thread runs. Gets data from queue, then sends through serial port and returns data through tcp if needed. Calls the transmogrifier class to check and formate data as needed.''' #print 'handleSer: Started', currentThread().getName() while(self.abortFlag == 0): _message = Transmogrify() # Instance of transmogrifier class socketReceived = self.dataQueue.get() # Blocking statement that gets data from queue and send to transmogrifier class serialSend = _message.inputData(socketReceived) #serialSend = _message.inputData(self.dataQueue.get()) # Blocking statement that gets data from queue and send to transmogrifier class # Callback to update statusbar and gauge msg = self.dataQueue.qsize() # Return queue size wx.CallAfter(self.window.updateQueue, msg) # Callback to gui thread to update statusbar and gauge # Serial try: self.ser.write(serialSend) # Write to serial port except: pass # Check if ArtBus command expects return data if _message.checkData(serialSend): serialReturn = self.ser.readline() socketReturn = _message.outputData(serialReturn, serialSend) # Read data from serial port and send to transmogrifier class # Check if return data from transmogrifier class was nill if socketReturn != 'nill': try: # TCP if self.protocol == 'tcp': self.clientsock.sendall(socketReturn) # Send data back throuch tcp connection # OSC if self.protocol == 'osc': oscAddress, oscValue = _message.createOscMessage(serialSend, serialReturn) osc.sendMsg(oscAddress, [int(oscValue)], self.oscHost, int(self.oscPort)) except(ValueError, IndexError): raise except: pass else: # If return data was nill flush serial ports to prevent data salad time.sleep(0.1); # this is the key in preventing data salad self.ser.flushInput() # flush input buffer self.ser.flushOutput() # flush output buffer time.sleep(0.0001) # Sleep to prevent thread come consuming cpu cycles class Transmogrify(): '''This class is what chews on the data and spits it back out. This is where the ArtBus specific protocol is carried out for the server to work with all the fancy media apps we all love.''' def inputData(self, socketReceived): '''This checks the input from the tcp connection.''' if socketReceived.startswith('!'): # Only accept ab commands socketReceived = socketReceived.strip() # Stip newline #print "TM input: %s" % socketReceived # I'm wondering if we should have this at all. if socketReceived.endswith(';'): serialMessage = socketReceived else: serialMessage = socketReceived + ';' return serialMessage def outputData(self, serialReturn, socketSend): '''This checks the data returned from the ArtBus and formates it to be returned through the tcp connection. This also traps unwanted data from ArtBus resets, accidental or not. This is where the tags or tokens will be implemented in the near future. When that happens this all will be restructed. The main that to know is if the return data from the ArtBus is not valid: startup, reset, error, or config mode, it must be set to 'nill'. It is 'nill' and not the python None object so that the same datatype is expected.''' # Checks if the returned data has a length greater than 0 and less than 5, this will change in the future try: if serialReturn[0].isdigit() or serialReturn[0].isalpha(): try: serialReturn = serialReturn.strip().split()[0] # Get rid of newlines etc # Check if the returned data starts with a character: startup, reset, error, or config mode. This will be easier with tags or tokens. if serialReturn[0].isalpha(): socketReturn = 'nill' else: socketSend = socketSend.strip().split()[0] # Get rid of newlines etc socketReturn = socketSend + serialReturn #print "TM output: %s" % socketReturn except: #(ValueError, IndexError): socketReturn = 'nill' else: socketReturn = 'nill' except(ValueError, IndexError): socketReturn = 'nill' return socketReturn def checkData(self, serialSend): '''This checks if the ArtBus command expects data to be returned.''' abCommand = serialSend[2:3] if (abCommand >= 'a' and abCommand <= 'f'): result = 1 else: result = 0 return result def createOscMessage(self, serialSend, serialReturn): '''This formates the OSC message.''' abAddress = serialSend[1:2] abCommand = serialSend[2:] abCommand = abCommand.replace(";","") serialReturn = serialReturn.strip() result = ("/" + abAddress + "/" + abCommand, str(serialReturn)) # OSC compliant slashed format return result class CharValidator(wx.PyValidator): '''This class subclasses wx.PyValidator and is used to validate the input for the controls.''' def __init__(self, flag): wx.PyValidator.__init__(self) self.flag = flag self.Bind(wx.EVT_CHAR, self.onChar) # Bind to wx event. def onChar(self, evt): '''Validates as user types.''' key = chr(evt.GetKeyCode()) if self.flag == 'no-alpha' and key in string.letters: return if self.flag == 'no-digit' and key in string.digits: return evt.Skip() class MainFrame(wx.Frame): '''Main gui class. This draws the widgets and starts everything. The following functions will be documented in the near future. The statusbar is not all that helpful right now, but I'm keeping it in.''' def __init__(self): wx.Frame.__init__(self, None, -1, 'Transmogrifier (%s)' % __version__, size=(345,325), style=wx.DEFAULT_FRAME_STYLE ^ (wx.MAXIMIZE_BOX | wx.RESIZE_BORDER)) self.threads = [] self.count = 0 self.dev_lt = [] self.baud_lt = '115200 19200' self.protocol_lt = 'osc tcp' self.serverLabel = ' Receive: ' self.getDevices() self.frame = (self) self.initStatusBar() #self.createMenuBar() self.panel = wx.Panel(self) # Create static text controls self.address_l = wx.StaticText(self.panel, -1, self.serverLabel) port_l = wx.StaticText(self.panel, -1, 'Port: ') oscAddress_l = wx.StaticText(self.panel, -1, ' Send: ') oscPort_l = wx.StaticText(self.panel, -1, 'Port: ') message_l = wx.StaticText(self.panel, -1, 'Message: ') #server_l = wx.StaticText(self.panel, -1, 'Address, Port:') #queue_l = wx.StaticText(self.panel, -1, 'Queue:') device_l = wx.StaticText(self.panel, -1, 'Serial Device: ') baud_l = wx.StaticText(self.panel, -1, ' Baud Rate: ') # Create text controls self.address_t = wx.TextCtrl(self.panel, validator=CharValidator('any'), size=(80, -1)) self.port_t = wx.TextCtrl(self.panel, validator=CharValidator('no-alpha'), size=(50, -1)) self.oscAddress_t = wx.TextCtrl(self.panel, validator=CharValidator('any'), size=(80, -1)) self.oscPort_t = wx.TextCtrl(self.panel, validator=CharValidator('no-alpha'), size=(50, -1)) self.message_t = wx.TextCtrl(self.panel, validator=CharValidator('any'), size=(175, -1)) self.device_cb = wx.Choice(self.panel, -1, choices=self.dev_lt) self.baud_cb = wx.Choice(self.panel, -1, choices=self.baud_lt.split()) # Create radio controls self.radio = wx.RadioBox(self.panel, -1, 'Protocol', (10,10), (62,80), self.protocol_lt.split(), 1, wx.RA_SPECIFY_COLS) # Create buttons self.start_btn = wx.Button(self.panel, label = 'Start') self.stop_btn = wx.Button(self.panel, label = 'Stop') # Create gauge self.gauge = wx.Gauge(self.panel, -1, 128, size=(200, 15), style=wx.GA_HORIZONTAL) self.gauge.SetBezelFace(3) self.gauge.SetShadowWidth(3) # Set control defaults self.protocol = 'osc' self.address_t.SetValue('127.0.0.1') self.port_t.SetValue('9000') self.oscAddress_t.SetValue('127.0.0.1') self.oscPort_t.SetValue('9001') self.message_t.SetValue('/toAB') self.statusbar.SetStatusText('Queue: 0', 0) self.statusbar.SetStatusText('Status: Configure settings', 1) # Bind events to controls and functions self.frame.Bind(wx.EVT_RADIOBOX, self.onRadio, self.radio) #self.frame.Bind(wx.EVT_TEXT, self.onAddressText, self.address_t) #self.frame.Bind(wx.EVT_TEXT, self.onPortText, self.port_t) self.frame.Bind(wx.EVT_TEXT, self.onMessage, self.message_t) #self.frame.Bind(wx.EVT_CHOICE, self.onDeviceChoice, self.device_cb) #self.frame.Bind(wx.EVT_CHOICE, self.onBaudChoice, self.baud_cb) self.frame.Bind(wx.EVT_BUTTON, self.onStartBtn, self.start_btn) self.frame.Bind(wx.EVT_BUTTON, self.onStopBtn, self.stop_btn) # Create main sizer mainSizer = wx.BoxSizer(wx.VERTICAL) #mainSizer.Add(wx.StaticLine(self.panel), 0, wx.EXPAND|wx.ALL, 5) # Create sub sizer subSizer = wx.BoxSizer(wx.HORIZONTAL) # Create radio sizer radioSizer = wx.BoxSizer(wx.VERTICAL) radioSizer.Add(self.radio) # Create server sizer serverSizer = wx.BoxSizer(wx.HORIZONTAL) serverSizer.Add((10,10), 0) serverSizer.Add(self.address_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) serverSizer.Add(self.address_t, 0) serverSizer.Add((10,10), 0) serverSizer.Add(port_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) serverSizer.Add(self.port_t, 0) serverSizer.Add((10,10), 0) # Create osc send sizer oscSizer = wx.BoxSizer(wx.HORIZONTAL) oscSizer.Add((10,10), 0) oscSizer.Add(oscAddress_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) oscSizer.Add(self.oscAddress_t, 0) oscSizer.Add((10,10), 0) oscSizer.Add(oscPort_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) oscSizer.Add(self.oscPort_t, 0) oscSizer.Add((10,10), 0) # Create message sizer messageSizer = wx.BoxSizer(wx.HORIZONTAL) messageSizer.Add((10,10), 0) messageSizer.Add(message_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) messageSizer.Add(self.message_t, 0) # Create serial sizer #serialSizer = wx.FlexGridSizer(1, 2, 5, 5) # rows, cols, vgap, hgap #serialBox = wx.StaticBox(self.panel, -1, 'Serial:') #serialSizer = wx.StaticBoxSizer(serialBox, wx.VERTICAL) #serialSizer.Add(device_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) #serialSizer.Add(self.device_cb, 0, wx.EXPAND) #serialSizer.Add(baud_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) #serialSizer.Add(self.baud_cb, 0, wx.EXPAND) #mainSizer.Add(serialSizer, 0, wx.EXPAND|wx.ALL, 5) # Create device sizer deviceSizer = wx.BoxSizer(wx.HORIZONTAL) deviceSizer.Add((10,10), 0) deviceSizer.Add(device_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) deviceSizer.Add(self.device_cb, 0, wx.EXPAND, 5) deviceSizer.Add((10,10), 1) # Create baud sizer baudSizer = wx.BoxSizer(wx.HORIZONTAL) baudSizer.Add((10,10), 0) baudSizer.Add(baud_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) baudSizer.Add(self.baud_cb, 0, wx.EXPAND) baudSizer.Add((10,10), 1) # Create gauge sizer gaugeSizer = wx.BoxSizer(wx.HORIZONTAL) gaugeSizer.Add((10,10), 1) gaugeSizer.Add(self.gauge, 0, wx.ALIGN_CENTER_VERTICAL) gaugeSizer.Add((10,10), 1) #mainSizer.Add(gaugeSizer, 0, wx.EXPAND|wx.ALL, 5) # Create bag sizer bagSizer = wx.GridBagSizer(hgap=8, vgap=8) bagSizer.Add(radioSizer, pos=(0,1), span=(3,1), flag=wx.EXPAND) bagSizer.Add(serverSizer, pos=(0,0), span=(1,1), flag=wx.EXPAND) bagSizer.Add(oscSizer, pos=(1,0), span=(1,1), flag=wx.EXPAND) bagSizer.Add(messageSizer, pos=(2,0), span=(1,1), flag=wx.EXPAND) #bagSizer.Add(wx.StaticLine(self.panel), pos=(3,0), span=(1,2), flag=wx.EXPAND) #bagSizer.Add(serialSizer, pos=(4,0), span=(1,1), flag=wx.EXPAND) #bagSizer.Add(gaugeSizer, pos=(0,4), span=(5,1), flag=wx.EXPAND) #subSizer.Add(bagSizer, 0, wx.EXPAND|wx.ALL, 5) #subSizer.Add((10,10), 0) #subSizer.Add(gaugeSizer, 0, wx.EXPAND, 5) mainSizer.Add((10,10), 0, wx.EXPAND|wx.ALL, 0) mainSizer.Add(bagSizer, 0, wx.EXPAND|wx.ALL, 5) mainSizer.Add(wx.StaticLine(self.panel), 0, wx.EXPAND|wx.ALL, 5) #mainSizer.Add(serialSizer, 0, wx.EXPAND|wx.ALL, 5) mainSizer.Add(deviceSizer, 0, wx.EXPAND|wx.ALL, 5) mainSizer.Add(baudSizer, 0, wx.EXPAND|wx.ALL, 5) mainSizer.Add(gaugeSizer, 0, wx.EXPAND|wx.ALL, 5) mainSizer.Add(subSizer, 0, wx.EXPAND|wx.ALL, 5) mainSizer.Add(wx.StaticLine(self.panel), 0, wx.EXPAND|wx.ALL, 10) # Create sub-sizer #subSizer = wx.BoxSizer(wx.HORIZONTAL) #subSizer.Add(self.address_t, 1) #subSizer.Add(self.port_t, 0, wx.LEFT|wx.RIGHT, 5) #serverSizer.Add(subSizer, 0, wx.EXPAND) #mainSizer.Add(wx.StaticLine(self.panel), 0, wx.EXPAND|wx.ALL, 5) # Create button sizer btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add((10,10), 1) btnSizer.Add(self.start_btn) btnSizer.Add((10,10), 0) btnSizer.Add(self.stop_btn) btnSizer.Add((10,10), 1) mainSizer.Add(btnSizer, 0, wx.EXPAND|wx.BOTTOM, 10) self.panel.SetSizer(mainSizer) self.allowInput(1) # Event functions def onRadio(self, event): '''Called when protocol is changed. Sets protocol and configures the gui.''' self.protocol = self.radio.GetStringSelection() self.setInput(self.protocol) #self.changeServerLabel(self.protocol) def onAddressText(self, event): self.statusbar.SetStatusText('Status: Enter a valid IP address', 1) def onPortText(self, event): self.statusbar.SetStatusText('Status: Enter a valid port number', 1) if self.port_t.GetValue() == '': #self.port_t.SetValue('9000') pass def onMessage(self, event): if self.message_t.GetValue() == '': self.message_t.SetValue('/') def onDeviceChoice(self, event): self.statusbar.SetStatusText('Status: Choose a serial device', 1) def onBaudChoice(self, event): self.statusbar.SetStatusText('Status: Choose a baud rate', 1) def onStartBtn(self, event): error = self.configSelections() if error == 1: self.allowInput(1) else: self.allowInput(0) self.statusbar.SetStatusText('Status: Starting', 1) self.statusbar.SetStatusText('Queue: 0', 0) self.count += 1 thread = WorkerThread(self.count, self, self.host, self.port, self.baud, self.dev, self.protocol) self.threads.append(thread) thread.setDaemon(1) thread.start() def onStopBtn(self, event): self.statusbar.SetStatusText('Status: Stopping', 1) self.stopThreads() self.allowInput(1) self.setInput(self.protocol) self.updateQueue(0) def onCloseWindow(self, event): self.Destroy() # Gui functions def configSelections(self): error = 0 self.dev = self.device_cb.GetStringSelection() self.dev = '/dev/' + self.dev self.baud = self.baud_cb.GetStringSelection() self.host = self.address_t.GetValue() self.port = int(self.port_t.GetValue()) self.oscHost = self.oscAddress_t.GetValue() self.oscPort = self.oscPort_t.GetValue() self.message = self.message_t.GetValue() if self.dev == '': error = 1 msg='Status: Choose serial device' elif self.dev == ('/dev/none availible') or (self.dev == '/dev/'): error = 1 msg='Status: No serial device' elif self.baud == '': error = 1 msg='Status: Choose baud rate' elif self.host == '': error = 1 msg='Status: Enter a valid IP address' elif self.port == '': error = 1 msg='Status: Enter a valid port' if error == 1: self.updateStatus(msg) #print 'error:', msg return error def setInput(self, protocol): # Disable server input based on protocol # TCP if protocol == 'tcp': self.address_t.Enable() self.port_t.Enable() self.oscAddress_t.Disable() self.oscPort_t.Disable() self.message_t.Disable() # OSC if protocol == 'osc': self.address_t.Enable() self.port_t.Enable() self.oscAddress_t.Enable() self.oscPort_t.Enable() self.message_t.Enable() def allowInput(self, status): # Disable input when running if status == 0: self.radio.Disable() self.address_t.Disable() self.port_t.Disable() self.oscAddress_t.Disable() self.oscPort_t.Disable() self.message_t.Disable() self.device_cb.Disable() self.baud_cb.Disable() self.start_btn.Disable() self.stop_btn.Enable() else: self.radio.Enable() self.address_t.Enable() self.port_t.Enable() self.oscAddress_t.Enable() self.oscPort_t.Enable() self.message_t.Enable() self.device_cb.Enable() self.baud_cb.Enable() self.start_btn.Enable() self.stop_btn.Disable() def changeServerLabel(self, protocol): # Change server label based on protocol # TCP if protocol == 'tcp': self.serverLabel = ' Server: ' #address_l = wx.StaticText(self.panel, -1, ' Server:') # OSC if protocol == 'osc': self.serverLabel = ' Receive: ' #address_l = wx.StaticText(self.panel, -1, ' Receive:') self.address_l.Update() self.address_l.Refresh() def getDevices(self): # This will only work on a *inux system foo_lt = [] dev_tmp = os.popen('ls /dev | grep -i usb') foo = dev_tmp.readlines() if len(foo) > 0: for f in (foo): f = f.strip('\n') foo_lt.append(f) for i in foo_lt: try: s = serial.Serial('/dev/%s' % i) self.dev_lt.append(i) s.close() except serial.SerialException: pass if self.dev_lt == []: self.dev_lt.append('none availible') # Thread functions def stopThreads(self): while self.threads: tNum = len(self.threads) - 1 thread = self.threads[tNum] thread.stop() self.threads.remove(thread) self.updateQueue(0) self.updateStatus('Status: Stopped') def setThreads(self, clientsock, ser, dataQueue): self.setSocket(clientsock, dataQueue) self.setSer(clientsock, ser, dataQueue) def setSocket(self, clientsock, dataQueue): self.count += 1 # TCP if self.protocol == 'tcp': thread = SocketThread(self.count, self, clientsock, dataQueue, self.protocol) # OSC if self.protocol == 'osc': thread = SocketThread(self.count, self, clientsock, dataQueue, self.protocol, self.message) self.threads.append(thread) thread.setDaemon(1) thread.start() def setSer(self, clientsock, ser, dataQueue): self.count += 1 # TCP if self.protocol == 'tcp': thread = SerThread(self.count, self, clientsock, ser, dataQueue, self.protocol) # OSC if self.protocol == 'osc': thread = SerThread(self.count, self, clientsock, ser, dataQueue, self.protocol, self.oscHost, self.oscPort) self.threads.append(thread) thread.setDaemon(1) thread.start() # Status bar functions def initStatusBar(self): self.statusbar = self.CreateStatusBar() self.statusbar.SetFieldsCount(2) self.statusbar.SetStatusWidths([-1, -2]) def updateStatus(self, msg): self.statusbar.SetStatusText(msg, 1) def updateQueue(self, msg): self.gauge.SetValue(msg) msg = 'Queue: %s' % str(msg) self.statusbar.SetStatusText(msg, 0) # Menu functions def menuData(self): return [('&File', (('', '', ''), ('About...', 'About', self.createAbout), ('&Quit', 'Quit', self.onCloseWindow)))] def createMenuBar(self): menuBar = wx.MenuBar() for eachMenuData in self.menuData(): menuLabel = eachMenuData[0] menuItems = eachMenuData[1] menuBar.Append(self.createMenu(menuItems), menuLabel) self.SetMenuBar(menuBar) def createMenu(self, menuData): menu = wx.Menu() for eachItem in menuData: if len(eachItem) == 2: label = eachItem[0] subMenu = self.createMenu(eachItem[1]) menu.AppendMenu(wx.NewId(), label, subMenu) else: self.createMenuItem(menu, *eachItem) return menu def createMenuItem(self, menu, label, status, handler, kind=wx.ITEM_NORMAL): if not label: menu.AppendSeparator() return menuItem = menu.Append(-1, label, status, kind) self.Bind(wx.EVT_MENU, handler, menuItem) def createAbout(self, event): dlg = About(self) dlg.ShowModal() dlg.Destroy() class About(wx.Dialog): text = ''' <html> <body> <p><b>Transmogrifier</b> is a application for the <b>ArtBus</b></p> <p>For more information about ArtBus visit: <a href="http://artbus.info">artbus.info</a></p> <p>ArtBus by <b>Ed Bennett</b><br/> <p>Authors:<br/> <ul> <li><b>Matthew Nelson</b></li> <li><b>Rob Drinkwater</b></li> <li><b>Ed Bennett</b></li> </ul></p> </body> </html> ''' def __init__(self, parent): wx.Dialog.__init__(self, parent, -1, 'About TCP to Serial', size=(250, 275)) html = wx.html.HtmlWindow(self) html.SetPage(self.text) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(html, 1, wx.EXPAND|wx.ALL, 5) self.SetSizer(sizer) self.Layout() if __name__ == '__main__': app = wx.PySimpleApp() MainFrame().Show() app.MainLoop()