#from animsetup import * # imports everything from animsetup, including global variables such as width, # height, delaytime. #!/usr/bin/env python # Animation Template. User must define a mydraw(brush,gc) import pygtk import gtk import gobject import time import threading from random import * width,height = 900,700 delaytime = 80 # 100ms delay between animation frames window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.set_title("Simplified Drawing Example") window.connect("destroy", lambda w: gtk.main_quit()) area = gtk.DrawingArea() area.set_size_request(width,height) window.resize(width,height) window.add(area) window.show_all() window.move(0,0) gc0 = area.window.new_gc() cmap = gc0.get_colormap() # common colors green = cmap.alloc_color(red=0,green=65535,blue=0) blue = cmap.alloc_color(red=0,green=0,blue=65535) red = cmap.alloc_color(red=65535,green=0,blue=0) white = cmap.alloc_color("white") black = cmap.alloc_color("black") gray = cmap.alloc_color("gray") yellow = cmap.alloc_color("yellow") purple = cmap.alloc_color("purple") gc0.set_foreground(green) gc0.set_background(blue) gc0.line_width = 1 # double buffering setup dbuf = gtk.gdk.Pixmap(area.window,width,height,-1) gc1 = dbuf.new_gc() gc1.set_colormap(cmap) ### Thread control syn = threading.Event() # throttles mainloop syn2 = threading.Event() # throttles user thread syn.clear() syn2.clear() stopall = False # for cleanup class MyThread ( threading.Thread ): def __init__(self,brush,gc): self.win = brush self.dgc = gc threading.Thread.__init__(self) def run ( self ): global mydraw mydraw(self.win,self.dgc) eehandler = 0 def area_expose_cb(area, event): global mainloop,eehandler my = MyThread(dbuf,gc1) my.start() # start student thread, which calls mydraw area.handler_block(eehandler) # prevent restart when window moves. mainloop(area.window) # start main animation refresh cycle return True # get ready for expose event eehandler = area.connect("expose_event", area_expose_cb) # animation refresh control loop: def mainloop(brush): if not stopall: syn.wait() # wait for permission to refresh screen syn.clear() # reset for next round brush.draw_drawable(gc0,dbuf,0,0,0,0,width,height) # refresh syn2.set() # informs user thread to goto next iteration eid = gobject.timeout_add(delaytime, mainloop, brush) # reschedule return False # end mainloop def updateDisplay(): syn.set() # release mainloop to refresh screen syn2.wait() # synch with mainloop before looping again syn2.clear() # reset for next round # end updateDisplay def cleanup(): global stopall stopall = True syn.set() syn2.set() # end cleanup # default mydraw function will be overriden #def mydraw(brush,gc): # print "default mydraw - should redefine your own" # default mydraw ########################################################################## def drawcircle(brush,gc,x,y,radius,fill): brush.draw_arc(gc,fill,x-radius,y-radius,2*radius,2*radius,0,360*64) # distance: def dist(x1,y1,x2,y2): dx = x1-x2 dy = y1-y2 return (dx*dx + dy*dy)**0.5 # make sure coordinates stay within width, height: def inbounds(x,y,rad): if (x(width-rad)): x = rad if (yheight-rad): y = rad return (x,y) # return the tuple x,y # end inbounds ### class for a stick figure class stickfigure: def __init__(self,x,y,scale,brush,gc): self.x = x self.y = y self.scale = scale self.color = blue # default color self.armfactor = 0 # straight arms self.mouthfactor = 1 # smile self.brush = brush # drawing window self.gc = gc # drawing graphical context self.calcpoints() # call procedure to set coordinates self.observers = [] # Be observee # __init__ def calcpoints(self): x,y,scale = self.x,self.y,self.scale # for convenience self.chest = (x,y) self.stomach = (x,y+scale) self.neck = (x,y-(scale/2)) self.nose = (x,self.neck[1]-(scale/2)) self.lleg = (x-scale,y+(2*scale)) self.rleg = (x+scale,y+(2*scale)) self.leye = (x-(scale/4),self.neck[1]-((3*scale)/4)) self.reye = (x+(scale/4),self.neck[1]-((3*scale)/4)) self.lmouth = (x-(scale/4),self.neck[1]-(scale/4)) self.rmouth = (x+(scale/4),self.neck[1]-(scale/4)) self.larm = (x-scale,y+(self.armfactor*scale/2)) self.rarm = (x+scale,y+(self.armfactor*scale/2)) self.lip = (x,self.neck[1]-(scale/4)+(self.mouthfactor*scale/8)) # calcpoints def draw(self): x,y,scale = self.x,self.y,self.scale # for convenience gc,brush = self.gc, self.brush leye,reye,lip = self.leye,self.reye,self.lip lmouth,rmouth,nose = self.lmouth,self.rmouth,self.nose neck,chest,stomach = self.neck,self.chest,self.stomach larm,rarm,lleg,rleg = self.larm,self.rarm,self.lleg,self.rleg gc.set_foreground(self.color) drawcircle(brush,gc,leye[0]-3,leye[1]-3,3,True) # left eye drawcircle(brush,gc,reye[0]-3,reye[1]-3,3,True) # right eye brush.draw_line(gc,lmouth[0],lmouth[1],lip[0],lip[1]) # mouth and lip brush.draw_line(gc,lip[0],lip[1],rmouth[0],rmouth[1]) drawcircle(brush,gc,nose[0],nose[1],3,False) # nose drawcircle(brush,gc,nose[0],nose[1],(scale/2),False) # new head brush.draw_line(gc,neck[0],neck[1],stomach[0],stomach[1]) # body brush.draw_line(gc,chest[0],chest[1],larm[0],larm[1]) # left arm brush.draw_line(gc,chest[0],chest[1],rarm[0],rarm[1]) # right arm brush.draw_line(gc,stomach[0],stomach[1],lleg[0],lleg[1]) # left leg brush.draw_line(gc,stomach[0],stomach[1],rleg[0],rleg[1]) # right leg # draw ## attribute methods: def armsup(self): self.armfactor = -1 self.calcpoints() # recalculate all coordinates def armsdown(self): self.armfactor = 1 self.calcpoints() def armsout(self): self.armfactor = 0 self.calcpoints() def besad(self): self.mouthfactor = -1 # lip is above mouth corners self.calcpoints() def move(self,dx,dy): self.x += dx self.y += dy self.x,self.y = inbounds(self.x,self.y,self.scale/2) self.calcpoints() self.notify() def setcolor(self,c): self.color = c ######## Observee Interface: def attach(self,obvr): self.observers.append(obvr) def detach(self,obvr): self.observers.remove(obvr) def notify(self): for x in self.observers: x.update(self) #notify # end class stickfigure ############ The Observer class is now a subclass of stickfigure class chasefig(stickfigure): def __init__(self,x,y,scale,brush,gc): stickfigure.__init__(self,x,y,scale,brush,gc) # call superclass cons. # init ## Observer interface: def update(self,observee): # synch with obsevee, chases observee dx, dy = 4,4 # determine movement vector if observee.x < self.x: dx = -4 # go left if observee.y < self.y: dy = -4 # go up self.move(dx,dy) # chase after observee #update # class chasefig def mydraw(brush,gc): # this function is called automatically. i = 0 fig1 = stickfigure(width-164,height/2,64,brush,gc) fig2 = chasefig(50,50,48,brush,gc) fig2.setcolor(red) fig1.attach(fig2) # attach fig2 to fig1 fig1.draw() fig2.draw() cx,cy = randint(-5,5),randint(-5,5) # fig1 movement vector while i<1000: gc.set_foreground(white) #clear background brush.draw_rectangle(gc,True,0,0,width,height) if (i%3) == 0: fig1.armsup() if (i%3) == 1: fig1.armsout() if (i%3) == 2: fig1.armsdown() fig1.move(cx,cy) fig1.draw() fig2.draw() # this is only call to fig2, which is an observer i+=1 # its moves are controled autonomously updateDisplay() # end while cleanup() # should be called after end of all animation # end mydraw ########################################################################## # The following line must be the last line in the program gtk.main()