Issac issac at
Fri Dec 28 10:35:57 CET 2001

Here's a networked drawing program for collaborative drawing with two people.

The first person to start the program runs the script with no arguments:

$ python 

The second person must specify the host, unless it's just localhost:

$ python

Anything you draw on your side will show up on your friend's side and vice-versa.



#!/usr/bin/env python
""" not exactly a paint prog. just a networked line drawing demo
version 0.0.0
paint program by dave michell.  networking added by issac (Dec. 2001).

import Tkinter,sys,socket,string,struct,thread

def recv_nbytes(sock, nbytes):
def recv_nbytes(sock, nbytes):
  Receive n bytes from the socket.
  _Effective TCP/IP_ p. 51 
  count = nbytes
  buf = ''
  while count > 0:
    chunk = sock.recv(count)
    assert len(chunk) <= count
    count = count - len(chunk)
    buf = buf + chunk
  assert len(buf) == nbytes
  return buf

class NetDrawApp:

  DEFAULT_PORT = 50007
  DEFAULT_HOST = 'localhost'
  MAX_NUM_CLIENTS = 1             # changing this will break the code a little

  def __init__(self):
    self.root = Tkinter.Tk()
    self.drawing_area = Tkinter.Canvas(self.root)
    self.drawing_area.bind("<Motion>", lambda evt, x=self: x.motion(evt))
    self.drawing_area.bind("<ButtonPress-1>", lambda evt, x=self: x.b1down(evt))
    self.drawing_area.bind("<ButtonRelease-1>", lambda evt, x=self: x.b1up(evt))

    self.b1 = "up"
    self.xold = None
    self.yold = None
    thread.start_new_thread(self.read_lines_from_socket, ())

  def read_lines_from_socket(self):
    while 1:
      # get the next line from the socket
      line_rec = recv_nbytes(self.sock, nbytes = 16)
      x0,y0,x1,y1 = struct.unpack('iiii', line_rec)
      #print 'got the line and drew it'

  def set_up_socket(self):
    if len(sys.argv) < 2: self.hostname = NetDrawApp.DEFAULT_HOST
    else:                 self.hostname = sys.argv[1]
    if len(sys.argv) < 3: self.portnum  = NetDrawApp.DEFAULT_PORT
    else:                 self.portnum  = string.atoi(sys.argv[2])

    self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

      # if peer is already up, act like a client
      self.sock.connect((self.hostname, self.portnum)) 
      print 'connected to '+self.hostname+' on port '+`self.portnum`
      print "waiting for connection on port "+`self.portnum`
      # peer is not up yet, so we'll just wait around like a server 
      self.listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      self.listen_sock.bind(('', self.portnum))
      self.sock, self.addr = self.listen_sock.accept()
      print 'got connection from '+`self.addr` 
  def b1down(self, event):
    self.b1 = "down"        # you only want to draw when the button is down
                            # because "Motion" events happen -all the time-

  def b1up(self, event):
    self.b1 = "up"
    self.xold = None        # reset the line when you let go of the button
    self.yold = None

  def motion(self, event):
    if self.b1 == "down":
      if self.xold != None and self.yold != None:
        # here's where you draw it. smooth. neat.
        event.widget.create_line(self.xold, self.yold, 
        line_rec = struct.pack('iiii', self.xold, self.yold, event.x, event.y)
        #print 'sending data'
        try:          self.sock.send(line_rec)
        except:       print 'error sending data: ',sys.exc_info()[0]
        else:         pass # print 'done sending data'
      self.xold = event.x
      self.yold = event.y

  def run(self):
    except Tkinter.TclError:
      try:      self.root.destroy() 
      except:   pass

def main():
  app = NetDrawApp()

if __name__ == "__main__":

Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (
Version: 6.0.310 / Virus Database: 171 - Release Date: 12/19/2001

More information about the Python-list mailing list