Module sketcher
[hide private]
[frames] | no frames]

Source Code for Module sketcher

  1  # sketcher.py 
  2  # Description:  This source contains the class and main application definitions for manipulating the 
  3  #               simulation and windowing environments. 
  4  # Environment:  Python 2.5.1 
  5  # Module used:  - sys 2.5.2 
  6  #               - pyode 1.20 
  7  #               - pygame 1.8.0 (with SDL.dll version 1.2.13.0) 
  8  #               - utils (provided) 
  9   
 10  # The application will not run unless the modules below are installed. 
 11  try: 
 12      import sys, ode, random, re, thread, traceback, psyco, pygame 
 13      from pygame.locals import * 
 14      from socket import * 
 15      from utils import * 
 16  except ImportError, errormsg: #if any module could not be loaded, then prompt error, and exit program 
 17      error('Unable to load one or more modules. %s' % errormsg) 
 18      raise SystemExit, 1 # exit program and return an error flag of value 1 to the operating system 
 19   
 20   
21 -class canvas(ode.World, pygame.sprite.RenderUpdates):
22 """This is a container class for holding the objects printed to the 'canvas' area of the application. 23 The position, physical properties (shapes), and orientation of each objects are held in this data 24 structure for easy management and manipulation around the canvas through user-driven actions and 25 the simulated actions of the virtual canvas environment. 26 Parameter: 27 - ode.World, supertype for extending containment of bodies and joints, and the environment physics. 28 - pygame.sprite.Group, supertype for extending containment of groups of objects in the environment."""
29 - def __new__(this, size=(640,480), color=Color('white'), image=None, song=None):
30 """Polymorphs the __new__ method in the base class ode.World so that other supertypes can be fully 31 initialized. Because ode.World is already a defined type (not a class), and therefore is 32 immutable, defining this function ensures that upon constructing objects of this subtype, the 33 __new__ method invokation will be done at the subtype level without conflicting the __init__ 34 requirements for initializing the other derived classes in its multi-inherited hierarchy. This is 35 similar to the way in which a string, int, and other immutable types can be extended even though 36 they are not directly treated as classes. Also, importantly, the parameters passed to this method 37 must match exactly with the parameters passed to the __init__ method. 38 Parameter: 39 - size, the canvas' size. 40 - color, the canvas' background color. 41 - image, the canvas' background picture.""" 42 return ode.World.__new__(this) # Explicitly invokes the supertype ode.Body's __new__ method
43 # with the appropriate parameters passed. 44
45 - def __init__(this, size=(640,480), color=Color('white'), image=None, song=None):
46 """Initialization procedure for defaulting the canvas' environment. 47 Parameter: 48 - size, the canvas' size. 49 - color, the canvas' background color. 50 - image, the canvas' background picture (passed as a string path to its location).""" 51 ode.World.__init__(this) 52 super(pygame.sprite.Group, this).__init__() # Proxy callback for accessing a superclass member. 53 this.size = size 54 this.background = pygame.Surface(size) 55 this.set_bgcolor(color) 56 if image is not None: this.set_bgimage(image, size) # Set background image only if one is passed. 57 this.bgcopy = this.background.copy() 58 this.set_bgmusic(song) 59 this.space = ode.Space() 60 this.setERP(100) 61 this.setCFM(1E-5) 62 this.play()
63
64 - def set_bgcolor(this, color):
65 """Sets the canvas' background color. 66 Parameter: 67 - color, the color to set the canvas' background to.""" 68 this.background.fill(color)
69
70 - def set_bgimage(this, image, size):
71 """Sets the canvas' background image. 72 Parameter: 73 - image, a path to the picture to set the canvas' background to.""" 74 this.background.blit(load_image(image, size), (0,0))
75
76 - def set_bgmusic(this, song):
77 """Sets the background music. 78 Parameter: 79 - song, the path of the background music to be played.""" 80 try: 81 load_song(song) 82 pygame.mixer.music.play(-1) 83 except: 84 pass
85
86 - def get_size(this):
87 """Returns a 2-tuple value of the canvas' screen size, as a (width,height) pair.""" 88 return this.size
89
90 - def is_sprite(this, pos):
91 """Tests whether an object is located at the current 'pos' location. 92 Parameter: 93 - pos, the (x,y) position where the object is located.""" 94 point = this.sprite(pygame.Surface((1,1)), pos, this) 95 return pygame.sprite.spritecollideany(point, this) is not None
96
97 - def get_sprite(this, pos):
98 """Returns the sprite that is located at the 'pos' location. Normally used when user selects and 99 object and wants to drag it from one area to another. 100 Parameter: 101 - pos, the (x,y) position where the object is located.""" 102 point = this.sprite(pygame.Surface((1,1)), pos, this) 103 return pygame.sprite.spritecollideany(point, this)
104
105 - def near_collision(this, contactgroup, geom1, geom2):
106 """Invoked whenever two objectas are about to collide, then creates collision points where 107 collision may occur. 108 Parameter: 109 - contactgroup, group for holding contact joints 110 - geom1, one of the geometry objects involved in the collision. 111 - geom2, one of the geometry objects involved in the collision.""" 112 for contact in ode.collide(geom1, geom2): 113 contact.setBounce(const.BOUNCE) 114 contact.setMu(const.FRICTION) 115 contact.setSoftCFM(1E-5) 116 contact.setSoftERP(1) 117 joint = ode.ContactJoint(this, contactgroup, contact) 118 joint.attach(geom1.getBody(), geom2.getBody())
119
120 - def step(this, framerate):
121 """Increments the world timestep, and detects near collisions. 122 Parameter: 123 - framerate, the refresh rate for updating the application window.""" 124 if this.gravity_on: 125 contactgroup = ode.JointGroup() 126 for i in range(const.CSIMULATE): 127 this.space.collide(contactgroup, this.near_collision) 128 ode.World.step(this, const.TIMESTEP/const.CSIMULATE) 129 contactgroup.empty()
130
131 - def pause(this):
132 """Pauses the simulation.""" 133 this.setGravity((0,0,0)) 134 this.gravity_on = False
135
136 - def play(this):
137 """Plays the simulation""" 138 this.setGravity((0,const.GRAVITY,0)) 139 this.gravity_on = True
140
141 - def add(this, image, topleft, vertices=None):
142 """Adds a new object to the canvas' environment. Once an object is added to the environment, it is 143 capable of interacting with other objects within the environment, and with user-driven inputs. 144 Parameter: 145 - object, the object to be registered to the canvas' environment. The object should contain 146 the basic configurations for representing its visual state as a surface; for the 147 area and position it occupies; and for the vertices it is composed from.""" 148 super(pygame.sprite.RenderUpdates, this).add(this.sketch(image, topleft, vertices, this))
149
150 - def sketch(this, image, topleft, vertices, world):
151 """Creates a new object given it's image copy, vertices, position, and world. 152 Parameter: 153 - image, a copy of the object. 154 - topleft, position of the object. 155 - vertices, points composing the object. 156 - world, the canvas the object belongs to.""" 157 body = this.sprite(image, topleft, this) 158 mass = ode.Mass() 159 mass.setBox(const.MDENSITY, const.MASS, const.MASS, const.MASS) 160 body.setMass(mass) 161 body.setPosition((topleft[0], topleft[1], 0)) 162 geom = ode.GeomBox(this.space, (image.get_size()[0], image.get_size()[1], 1)) 163 geom.setBody(body) 164 return body
165 166
167 - class sprite(ode.Body, pygame.sprite.Sprite):
168 """Abstract object represented in an area of the canvas. Objects can either be drawn manually by the 169 user, or be imported from an image file. The resulting objects are also represented as surfaces, 170 occupying a given region of the canvas. 171 Parameter: 172 - ode.Body, supertype for extending containment of physical properties of a body/object. 173 - pygame.sprite.Sprite, supertype for extending containment of sprites/objects in the environment."""
174 - def __new__(this, image, rect, world):
175 """Polymorphs the __new__ method in the base class ode.Body so that other supertypes can be fully 176 initialized. Because ode.Body is already a defined type (not a class), and therefore is 177 immutable, defining this function ensures that upon constructing objects of this subtype, the 178 __new__ method invokation will be done at the subtype level without conflicting the __init__ 179 requirements for initializing the other derived classes in its multi-inherited hierarchy. This 180 is similar to the way in which a string, int, and other immutable types can be extended even 181 though they are not directly treated as classes. Also, importantly, the parameters passed to 182 this method must match exactly with the parameters passed to the __init__ method. 183 Parameter: 184 - _object, the object to be represented in a canvas world; this contains the initial 185 configuration of an object belonging to a canvas' environment. 186 - world, the canvas the object belongs to (membership constraint).""" 187 return ode.Body.__new__(this, world) # Explicitly invokes the supertype ode.Body's __new__
188 # method with the appropriate parameters passed. 189
190 - def __init__(this, image, topleft, world):
191 """Initialization procedure for defaulting the object's general configuration. 192 Parameter: 193 - _object, the object to be represented in a canvas world; this contains the configuration 194 for object's initial state in the world. 195 - world, the canvas the object belongs to (membership constraint).""" 196 ode.Body.__init__(this, world) 197 pygame.sprite.Sprite.__init__(this) 198 this.mass = None 199 this.image = image 200 this.rect = image.get_rect(); 201 if topleft is not None: this.rect.topleft = topleft
202
203 - def update(this, x, y):
204 """Updates the object's current position. 205 Parameter: 206 - x, the x-position of the object. 207 - y, the y-position of the object.""" 208 this.rect.move_ip(x, y) 209 this.setPosition((this.rect.left+x, this.rect.top+y, 0))
210 211
212 -class workspace(pygame.sprite.Sprite):
213 """This is the container class for holding and loading the tools, and general GUI layers, of the application. 214 Some of the tools that may be included are functions for adjusting the input pen style and size, the canvas 215 repository, etc., along with their associated GUI icons and properties.""" 216
217 - def __init__(this, canvas=None, image=None, rect=(0,0,128,256), alpha=255):
218 """Initializes the workspace. 219 Parameter: 220 - canvas, the canvas the workspace belongs to. 221 - the background image of the workspace. 222 - the position and demensions (x,y,width,height) of the workspace. 223 - alpha, applies transparency to the workspace background.""" 224 pygame.sprite.Sprite.__init__(this) 225 this.canvas = canvas 226 if image is None: 227 this.image = pygame.Surface((rect[2], rect[3])) 228 this.image.fill(Color('white')) 229 else: 230 this.image = load_image(image, (rect[2], rect[3])) 231 this.image.set_alpha(alpha) 232 this.rect = rect
233
234 - def show(this):
235 """Shows the workspace on top of canvas.""" 236 if this.canvas is not None: 237 this.canvas.background.blit(this.image, (0,0)) 238 else: 239 pass
240 241
242 -class network:
243 """This is the class for controlling the network communication between multiple online canvases inbounded on 244 the current machine, or outbounded to multiple remote machines. The primary goal of this class is to 245 establish and maintain multiple connections by spawning a single server process thread/daemon which waits 246 for incoming/inbounding connections and acknowledging them upon the user's grant; and to make 247 outgoing/outbounding requests to a remote system based on the user's request. The application may be in 248 several states at a time: it may act as a server without having a session with another machine; it may 249 may be a server without an inbound connection, but an established outbounded connection with one or more 250 remote machines; it may be a server with inbounded connections, but no outbounded connections; or it may 251 be both connected inboundly and outboundly.""" 252
253 - def __init__(this):
254 """Initializes the network, by creating a new server daemon process.""" 255 this.ip = gethostbyaddr(gethostname())[-1][0] 256 this.clients = [] 257 this.s = socket(AF_INET, SOCK_STREAM) 258 this.server(const.PORT)
259
260 - def client(this, host, port=const.PORT):
261 """Connects to a remote host machine, given it's ip and, optionally, it's port number.""" 262 try: 263 this.s.connect((host, port)) 264 except: 265 #traceback.print_exc() 266 error('Unable to connect to \'%s\'' % host)
267
268 - def server(this, port):
269 """Starts-up the server on the current machine, by binding a sever daeom process to the a ip address, and 270 port number. 271 Parameter: 272 - port, the port number to bound the server process.""" 273 try: 274 #this.s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 275 this.s.bind((this.ip, port)) 276 this.s.listen(5) 277 thread.start_new(this.serverthrd, ()) 278 except: 279 #traceback.print_exc() 280 error('Unable to start server')
281
282 - def serverthrd(this):
283 """Server thread used to act independently of the current user processes. Each thread is dedicated to a single 284 connecting remote client machine - the events passed from that machine are pushed to the current hosting 285 machine.""" 286 c = this.s.accept() 287 this.clients.append(c) 288 thread.start_new(this.serverthrd, ()) 289 this.receive(c)
290
291 - def send(this, event):
292 """Sends an event to a remote machine. The respective machines will then push this event to their event stack to 293 to be handled has if the current sending machine is directly interacting with the remote machine. 294 Parameter: 295 - event, the event object to be transmitted to a remote client.""" 296 for c in this.clients: 297 c[0].send(event + '\n')
298
299 - def receive(this, c):
300 """A server thread immediately jumps into receving mode, where in the lifetime of its connection with the remote 301 client machine, it receives and queues each event that is transmitted. 302 Parameter: 303 - c, the (socket, address) pair of the client machine to receive events from.""" 304 data = [] 305 while True: 306 try: 307 data.append(c[0].recv(1)) 308 if(data[-1] == '\n'): 309 event_regexp = re.match("^<Event\((\d*).*({\'ip\':.*: )<Event\((\d*).*({.*})\)\>(}).*$", str(data)) 310 event = pygame.event.Event(int(y.group(1)), eval(y.group(2) + "\'\'" + y.group(5))) 311 sub_event = str(pygame.event.Event(int(y.group(3)), eval(y.group(4)))) 312 event.update({'val': sub_event}) 313 pygame.event.post(event) 314 data = [] 315 except: 316 #traceback.print_exc() 317 c.close() 318 this.clients.remove(c) 319 break
320
321 -def update():
322 """Updates the application window with the changes made in the canvas world.""" 323 window.blit(sketchpad.background, (0,0)) 324 #window.blit(_workspace.image, (0,0)) 325 pygame.display.update()
326
327 -def cmdline():
328 """Handles command-line line requests.""" 329 while True: 330 try: 331 cmd = re.match("^\s*(\w*)\s*(.*)$", raw_input(">> ")) 332 if cmd.group(1) == "connect": 333 ip = re.match("^(\d+\.\d+\.\d+\.\d+)\s*(:\s*(\d+))?\s*$", cmd.group(2)) 334 if ip == None: 335 ip = re.match("^(\d+\.\d+\.\d+\.\d+)\s*(:\s*(\d+))?\s*$", raw_input("host ip: ")) 336 while ip == None: 337 print 'Error: Invalid host IPv4 ip address format.' 338 ip = re.match("^(\d+\.\d+\.\d+\.\d+)\s*(:\s*(\d+))?\s*$", raw_input("host ip: ")) 339 if ip.groups()[2] != None: 340 _network.client(ip.group(1), ip.group(3)) 341 else: 342 _network.client(ip.group(1)) 343 elif cmd.group(1) == "load": 344 path = re.match("^\s*(\S*)\s*$", cmd.group(2)) 345 if path.group(1) == '': 346 path = re.match("^\s*(\S*)\s*$", raw_input("image path: ")) 347 while path == None: 348 print 'Error: image path must be specified.' 349 path = re.match("^\s*(\S*)\s*$", raw_input("image path: ")) 350 path = load_image(path.group(1)) 351 sketchpad.add(path, (0,0)) 352 elif cmd.group(1) == "shape": 353 shape = cmd.group(2).strip() 354 if shape in const.SHAPES: 355 shapetype = shape 356 else: 357 try: 358 load_image(shape) 359 shapetype = shape 360 except: 361 pass 362 setshape(shapetype) 363 elif cmd.group(1) == "play": 364 sketchpad.play() 365 print 'gravity on' 366 elif cmd.group(1) == "pause": 367 sketchpad.pause() 368 print 'gravity off' 369 elif cmd.group(1) in ("quit", "exit"): 370 pygame.event.post(pygame.event.Event(pygame.QUIT, [])) 371 else: 372 print 'Invalid command \'%s\'' % cmd.group(1) 373 except: 374 pass
375 376
377 -def getshape(vertices):
378 """Returns a shape given a set of vertices. Currently, shapes are predefined, where only circles, boxes, and 379 custom user images can be returned. 380 Parameter: 381 - vertices, the vertices to use to construct the new shape.""" 382 color = const.POINT_RCOLOR[random.randint(0, len(const.POINT_RCOLOR)-1)] #random selects a color pair. 383 rect = pygame.draw.lines(sketchpad.background.copy(), const.POINT_COLOR, False, vertices, const.POINT_SIZE) 384 screen = pygame.Surface((rect.width+1, rect.height+1)) 385 screen.blit(sketchpad.bgcopy, (0,0), rect) 386 if shapetype in const.SHAPES: 387 if shapetype == 'box': 388 pygame.draw.rect(screen, Color(color[0]), ((0,0), rect.size), 0) 389 pygame.draw.rect(screen, Color(color[1]), ((0,0), rect.size), const.POINT_SIZE) 390 elif shapetype == 'circle': 391 pygame.draw.ellipse(screen, Color(color[0]), ((0,0), rect.size), 0) 392 pygame.draw.ellipse(screen, Color(color[1]), ((0,0), rect.size), const.POINT_SIZE) 393 else: 394 load_image(shapetype, rect.size) 395 return (rect, screen)
396 397
398 -def event_loop(framerate=30):
399 """The application's main event loop for controlling general user and application-driven events. 400 The events that are handled are as follow: 401 - Closing application window: 402 - by clicking the application window's close button 403 - by pressing ALT + F4 404 - Resizing application window: 405 - by clicking the application window's maximize and normalize buttons 406 - by pressing ALT + ENTER 407 - by pressing ESC (to revert from fullscreen mode to normal mode) 408 - by pressing F (to switch between fullscreen and normal modes) 409 - Registering canvas inputs: 410 - by clicking the left mouse-click button, and then moving the mouse at least two pixel points over 411 an area of the canvas, and then finally releasing the left mouse-click button. The registered inputs 412 are stored as a 2-tuple list of (x,y) vertices for each pixel points/path tranversed during the left 413 mouse-click hold and drag events. 414 Parameter: 415 - framerate, sets the frame rate at which the application window refreshes, and at which, for each refresh 416 interval, all updates are carried out. Also, the canvas environment's time step increases by a fraction 417 of 1/framerate. The higher the frame rate, the faster the animation of objects in the canvas environment.""" 418 clock = pygame.time.Clock() 419 getinput = {_network.ip: {'record': False, 'drag': False, 'id': None, 'screen': None, 'vertices': []}} 420 done = False # Event loop sentinel; when true, all user and application-driven events will be ignored. 421 422 while not done: 423 for event in pygame.event.get(): # iterates through all events currently in the event-queue. 424 if event.type == USEREVENT: 425 getinput.update({ip: {'record': False, 'drag': False, 'id': None, 'screen': None, 'vertices': []}}) 426 ip = event.ip 427 event = event.val 428 else: 429 _network.send(str(pygame.event.Event(pygame.USEREVENT, ip=_network.ip, val=event))) 430 ip = _network.ip 431 432 if event.type == KEYDOWN: # triggered by user inputs received via keyboard key inputs 433 # handles various contexts where the user may resize the program window using one or more key inputs. 434 if event.key == K_f or ((pygame.key.get_mods() & KMOD_ALT) and pygame.key.get_pressed()[pygame.K_RETURN]): 435 if(window.get_flags() & FULLSCREEN): 436 pygame.display.set_mode(const.WINDOW_SIZE, RESIZABLE) 437 else: 438 pygame.display.set_mode(sketchpad.size, FULLSCREEN) 439 elif event.key == K_ESCAPE and window.get_flags() & FULLSCREEN: 440 pygame.display.set_mode(const.WINDOW_SIZE, RESIZABLE) 441 elif (pygame.key.get_mods() & KMOD_ALT) and pygame.key.get_pressed()[pygame.K_F4]: 442 done = True 443 break 444 elif event.type == QUIT: # triggered by application window's close button. 445 done = True 446 break 447 elif event.type == VIDEOEXPOSE: # triggered each time the application window appears in the foreground. 448 pass 449 elif event.type == VIDEORESIZE: # triggered each time the application window is resized. 450 pygame.display.set_mode(event.size, RESIZABLE) 451 elif event.type == MOUSEBUTTONDOWN: # triggered each time a mouse click occurs. 452 if event.button == 1: # If the user clicked the left mouse button. 453 if sketchpad.is_sprite(event.pos): 454 getinput[ip].update({'drag': True, 'id': sketchpad.get_sprite(event.pos), 'vertices': [event.pos]}) 455 getinput[ip]['id'].disable() 456 else: 457 # Change the record mode state to on, and record the first input as a vertix of the position of 458 # the pixel within the canvas from which the user left mouse-clicked on. 459 getinput[ip].update({'record': True, 'vertices': [event.pos]}) 460 elif event.button == 3: 461 if sketchpad.is_sprite(event.pos): 462 sketchpad.get_sprite(event.pos).kill() 463 sketchpad.clear(sketchpad.background, sketchpad.bgcopy) 464 sketchpad.draw(sketchpad.background) 465 elif event.type == MOUSEBUTTONUP: # triggered each time a mouse click is released. 466 # Test whether the record state was on; if so, then assume there were recorded inputs. 467 if getinput[ip]['record'] is True: 468 # Catches a special-case error; occurs when the user only click-and-release on a single 469 # pixel/vertix. 470 try: 471 rect, getinput[ip]['screen'] = getshape(getinput[ip]['vertices']) 472 sketchpad.background.blit(sketchpad.bgcopy, (0,0)) 473 sketchpad.add(getinput[ip]['screen'], rect.topleft, getinput[ip]['vertices']) 474 sketchpad.draw(sketchpad.background) 475 getinput[ip]['screen'] = None 476 except Exception, err: 477 #traceback.print_exc() 478 pass 479 getinput[ip]['record'] = False # Resets the record state to off, implying no further drawing actions. 480 elif getinput[ip]['drag'] is True: 481 getinput[ip]['id'].setLinearVel((0,0,0)) 482 getinput[ip]['id'].enable() 483 getinput[ip]['drag'] = False 484 elif event.type == MOUSEMOTION: # triggered each time the mouse pointer is moved. 485 # Test whether record mode is on; if so, then append the new mouse motion/position to the input 486 # record/vertices list. 487 if getinput[ip]['record'] is True: 488 # appends the new vertix position as the current mouse position, and dynamically connects (draws), 489 # a line from the previous vertix position recorded in the list, to the new vertix position moved 490 # in current mouse position, thus, showing a visible trailing path as a result of these actions. 491 getinput[ip]['vertices'].append(event.pos) 492 pygame.draw.lines(sketchpad.background, const.POINT_COLOR, True, [getinput[ip]['vertices'][-2], getinput[ip]['vertices'][-1]], const.POINT_SIZE) 493 494 if len(getinput[ip]['vertices']) > 2: 495 getinput[ip]['vertices'][-3:] = optimize(getinput[ip]['vertices'][-3:]) 496 if getinput[ip]['drag'] is True: 497 try: 498 getinput[ip]['vertices'].append(event.pos) 499 x,y = getinput[ip]['vertices'][-1][0] - getinput[ip]['vertices'][-2][0], getinput[ip]['vertices'][-1][1] - getinput[ip]['vertices'][-2][1] 500 sketchpad.clear(sketchpad.background, sketchpad.bgcopy) 501 getinput[ip]['id'].update(x,y) 502 sketchpad.draw(sketchpad.background) 503 except: 504 #traceback.print_exc() 505 pass 506 507 for body in sketchpad: 508 if getinput[ip]['drag'] == True and getinput[ip]['id'] == body: 509 pass 510 else: 511 if sketchpad.background.get_rect().colliderect(body.rect): 512 sketchpad.clear(sketchpad.background, sketchpad.bgcopy) 513 body.rect.left, body.rect.top = body.getPosition()[:2] 514 sketchpad.draw(sketchpad.background) 515 else: 516 body.kill() 517 518 update() 519 sketchpad.step(framerate) 520 clock.tick(framerate) # Refreshes the frame by framerate amount of time units.
521 522
523 -def main():
524 """This is the main application method for initializing the application window upon program startup, and 525 for configuring a sketchpad/canvas for use within the window.""" 526 psyco.full() 527 pygame.init() 528 pygame.display.set_caption(const.WINDOW_TITLE) 529 530 global window, sketchpad, _workspace, _network, shapetype 531 window = pygame.display.set_mode(const.WINDOW_SIZE, RESIZABLE) # creates a resizable application window of a certain demention. 532 sketchpad = canvas(max(pygame.display.list_modes()), const.CANVAS_COLOR, const.CANVAS_BGIMG, const.CANVAS_BGSONG) # creates and initializes a canvas identified as 'sketchpad'. 533 534 #_workspace = workspace(sketchpad, const.WORKSPACE, alpha=25) 535 #_workspace.show() 536 537 _network = network() # initializes the network by starting a server thread process to wait on a connecting client. 538 539 shapetype = 'box' 540 541 thread.start_new(cmdline, ()) # starts the command-line handling in a separate thread. 542 543 event_loop(const.FRAMERATE) # start monitering user and application events. 544 pygame.quit() #unload the pygame module for proper program exit.
545 546 547 # Do not invoke main() unless the '__main__' key is defined in the application environment. 548 if __name__ == '__main__': 549 main() 550