#### This program was created to allow introductory level students to write ## animation programs using a simple while loop, bypassing the event-driven ## model of GTK. The function students write is called 'mydraw()', which ## has access to the cairo-context 'brush', as well as global variables ## 'width' and 'height' that represent the dimensions of the window. ## The screen is refreshed when updateDisplay() is called. import cairo import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk, GLib, GObject import threading import time import _thread #### globals width,height = 1024,768 win = Gtk.Window() drawingarea = Gtk.DrawingArea() win.add(drawingarea) brush = 1 da = 1 delaytime = 0.5 # default delay time half second eehandler = 0 ### Thread control syn = threading.Event() # throttles mainloop syn2 = threading.Event() # throttles user thread syn.clear() # .set allows to go through, .clear blocks syn2.clear() # block stopall = False # for cleanup # Thread permits a while loop inside mydraw class MyThread ( threading.Thread ): def __init__(self,d,b): global brush,da threading.Thread.__init__(self) self.da = d self.br = b brush = b da = d def run ( self ): global mydraw mydraw() #class MyThread # animation refresh control loop: def mainloop(da1, brush1): global da,brush brush = brush1 # print("mainloop") if not stopall: win.present() # needed for MacOS syn.wait() # wait for permission to refresh screen syn.clear() # reset for next round brush.stroke() syn2.set() # informs user thread to goto next iteration win.queue_draw_area(0,0,width-1,height-1) return False # end mainloop def area_expose_cb(daa, bbrush): global brush, da, eehandler,mainloop brush = bbrush my = MyThread(daa,bbrush) my.start() drawingarea.handler_block(eehandler) eeh = drawingarea.connect('draw', mainloop) # reconnect #win.queue_draw() mainloop(daa,bbrush) # sxxtart main animation refresh cycle return True win.connect('destroy', lambda w: Gtk.main_quit()) win.set_default_size(width,height) eehandler = drawingarea.connect('draw', area_expose_cb) win.show_all() 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 ########################################################################## ############################# STUDENT CODE ############################### ########################################################################## ## To affect animation, entire image must be redrawn with each updateDisplay def mydraw(): scale = 0 # controls size of rectangle brush.move_to(10,10) while scale<300: brush.set_source_rgb(0,0,0) brush.paint() # clears background (paints it black) brush.set_source_rgb(0,1,0) # green brush.rectangle(10,10,600-scale*2,400-scale) #brush.fill() # draws solid rectangle scale = scale + 20 # last two lines of the while loop must be as follows: updateDisplay() time.sleep(0.2) # delay for 0.2 seconds #animation loop #mydraw #### The following must be the last line of the program: don't move Gtk.main()