from Tkinter import *
from socket import *
import cPickle, threading, sys
# Each message is a command + data
CMD_JOINED,CMD_LEFT,CMD_MSG,CMD_LINE,CMD_JOINRESP = range(5)
people = {} # key = (ipaddr,port), value = (name,color)
# Draw a temporary line
lastLine = \
canvas.create_line(dx,dy,mx,my,width=2,fill='Black')
def onBUp(e):
'User released left mouse button'
canvas.delete(lastLine) # Erase the temporary line
canvas.unbind('<Motion>') # No more move msgs, please!
# Send out the draw-a-line command
sendMsg(chr(CMD_LINE)+cPickle.dumps((dx,dy,e.x,e.y),1))
def onEnter(foo):
'User hit the [Enter] key'
sendMsg(chr(CMD_MSG)+entry.get())
entry.delete(0,END) # Clear the entry widget
def setup(root):
'Creates the user interface'
global msgs,entry,canvas
# The big window holding everybody's messages
msgs = Text(root,width=60,height=20)
msgs.grid(row=0,col=0,columnspan=3)
# Hook up a scrollbar to see old messages
s = Scrollbar(root,orient=VERTICAL)
s.config(command=msgs.yview)
msgs.config(yscrollcommand=s.set)
s.grid(row=0,col=3,sticky=N+S)
# Where you type your message
entry = Entry(root)
entry.grid(row=1,col=0,columnspan=2,sticky=W+E)
entry.bind('<Return>',onEnter)
entry.focus_set()
b = Button(root,text='Quit',command=onQuit)
b.grid(row=1,col=2)
# A place to draw
canvas = Canvas(root,bg='White')
canvas.grid(row=0,col=5)
# Notify me of button press and release messages
canvas.bind('<ButtonPress-1>',onBDown)
canvas.bind('<ButtonRelease-1>',onBUp)
def msgThread(addr,port,name):
'Listens for and processes messages'
# Create a listen socket
s = socket(AF_INET, SOCK_DGRAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('',port))
# Join the multicast group
s.setsockopt(SOL_IP,IP_ADD_MEMBERSHIP,\
inet_aton(addr)+inet_aton(''))
while 1:
# Get a msg and strip off the command byte
msg,msgFrom = s.recvfrom(2048)
cmd,msg = ord(msg[0]),msg[1:]
if cmd == CMD_JOINED: # New join
msgs.insert(END,'(%s joined the chat)\n' % msg)
elif cmd == CMD_LEFT: # Somebody left
who = people[msgFrom][0]
if who == name: # Hey, _I_ left, better quit
break
msgs.insert(END,'(%s left the chat)\n' % \
who,'color_'+who)
elif cmd == CMD_MSG: # New message to display
who = people[msgFrom][0]
msgs.insert(END,who,'color_%s' % who)
msgs.insert(END,': %s\n' % msg)
elif cmd == CMD_LINE: # Draw a line
dx,dy,ex,ey = cPickle.loads(msg)
canvas.create_line(dx,dy,ex,ey,width=2,\
fill=people[msgFrom][1])
# Create a tag to draw text in their color
msgs.tag_configure('color_' + who,foreground=color)
# Leave the multicast group
s.setsockopt(SOL_IP,IP_DROP_MEMBERSHIP,\
inet_aton(addr)+inet_aton(''))
if __name__ == '__main__':
argv = sys.argv
if len(argv) < 3:
print 'Usage:',argv[0],'<name> <color> '\
'[addr=<multicast address>] [port=<port>]'
sys.exit(1)
global name, addr, port, myColor
addr = '235.0.50.5' # Default IP address
port = 54321 # Default port
name,myColor = argv[1:3]
for arg in argv[3:]:
if arg.startswith('addr='):
addr = arg[len('addr='):]
elif arg.startswith('port='):
port = int(arg[len('port='):])
# Start up a thread to process messages
threading.Thread(target=msgThread,\
args=(addr,port,name)).start()
# This is the socket over which we send out messages
global sendSock
sendSock = socket(AF_INET,SOCK_DGRAM)
sendSock.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
sendSock.connect((addr,port))
# Don't let the packets die too soon
sendSock.setsockopt(SOL_IP,IP_MULTICAST_TTL,2)
# Create a Tk window and create the GUI
root = Tk()
root.title('%s chatting on channel %s:%d' % \
(name,addr,port))
setup(root)
# Join the chat!
sendMsg(chr(CMD_JOINED)+name)
root.mainloop()