#!/usr/bin/python """ This module is part of Swampy, a suite of programs available from allendowney.com/swampy. Copyright 2005 Allen B. Downey Distributed under the GNU General Public License at gnu.org/licenses/gpl.html. """ import math import random import time import threading import sys import Tkinter from Gui import Gui class World(Gui): """Represents the environment where Animals live. A World usually includes a canvas, where animals are drawn, and sometimes a control panel. """ current_world = None def __init__(self, delay=0.5, *args, **kwds): Gui.__init__(self, *args, **kwds) self.delay = delay self.title('World') # keep track of the most recent world World.current_world = self # set to False when the user presses quit. self.exists = True # list of animals that live in this world. self.animals = [] # if the user closes the window, shut down cleanly self.protocol("WM_DELETE_WINDOW", self.quit) def wait_for_user(self): """Waits for user events and processes them.""" try: self.mainloop() except KeyboardInterrupt: print 'KeyboardInterrupt' def quit(self): """Shuts down the World.""" # tell other threads that the world is gone self.exists = False # destroy closes the window self.destroy() # quit terminates mainloop (but since mainloop can get called # recursively, quitting once might not be enough!) Gui.quit(self) def sleep(self): """Updates the GUI and sleeps. Calling Tk.update from a function that might be invoked by an event handler is generally considered a bad idea. For a discussion, see http://wiki.tcl.tk/1255 However, in this case: 1) It is by far the simplest option, and I want to keep this code readable. 2) It is generally the last thing that happens in an event handler. So any changes that happen during the update won't cause problems when it returns. Sleeping is also a potential problem, since the GUI is unresponsive while sleeping. So it is probably a good idea to keep delay less than about 0.5 seconds. """ self.update() time.sleep(self.delay) def register(self, animal): """Adds a new animal to the world.""" self.animals.append(animal) def unregister(self, animal): """Removes an animal from the world.""" self.animals.remove(animal) def clear(self): """Undraws and removes all the animals. And deletes anything else on the canvas. """ for animal in self.animals: animal.undraw() self.animals = [] try: self.canvas.delete('all') except AttributeError: print 'Warning: World.clear: World must have a canvas.' def step(self): """Invoke the step method on every animal.""" for animal in self.animals: animal.step() def run(self): """Invoke step intermittently until the user presses Quit or Stop.""" self.running = True while self.exists and self.running: self.step() self.update() def stop(self): """Stops running.""" self.running = False def map_animals(self, callable): """Apply the given callable to all animals. Args: callable: any callable object, including Gui.Callable """ return map(callable, self.animals) def make_interpreter(self, gs=None): """Makes an interpreter for this world. Creates an attribute named inter. """ self.inter = Interpreter(self, gs) def run_text(self): """Executes the code from the TextEntry in the control panel. Precondition: self must have an Interpreter and a text entry. """ source = self.te_code.get(1.0, Tkinter.END) self.inter.run_code(source, '') def run_file(self): """Read the code from the filename in the entry and runs it. Precondition: self must have an Interpreter and a filename entry. """ filename = self.en_file.get() fp = open(filename) source = fp.read() self.inter.run_code(source, filename) class Interpreter(object): """Encapsulates the environment where user-provided code executes.""" def __init__(self, world, gs=None): self.world = world # if the caller didn't provide globals, use the current env if gs == None: self.globals = globals() else: self.globals = gs def run_code_thread(self, *args): """Runs the given code in a new thread.""" return MyThread(self.run_code, *args) def run_code(self, source, filename): """Runs the given code in the saved environment.""" code = compile(source, filename, 'exec') try: exec code in self.globals except KeyboardInterrupt: self.world.quit() except Tkinter.TclError: pass class MyThread(threading.Thread): """Wrapper for threading.Thread. Improves the syntax for creating and starting threads. """ def __init__(self, target, *args): threading.Thread.__init__(self, target=target, args=args) self.start() class Animal(object): """Abstract class, defines the methods child classes need to provide. Attributes: world: reference to the World the animal lives in. x: location in Canvas coordinates y: location in Canvas coordinates """ def __init__(self, world=None): self.world = world or World.current_world if self.world: self.world.register(self) self.x = 0 self.y = 0 def set_delay(self, delay): """Sets delay for this animal's world. delay is made available as an animal attribute for backward compatibility; ideally it should be considered an attribute of the world, not an animal. Args: delay: float delay in seconds """ self.world.delay = delay delay = property(lambda self: self.world.delay, set_delay) def step(self): """Takes one step. Subclasses should override this method. """ pass def draw(self): """Draws the animal. Subclasses should override this method. """ pass def undraw(self): """Undraws the animal.""" if self.world.exists and hasattr(self, 'tag'): self.world.canvas.delete(self.tag) def die(self): """Removes the animal from the world and undraws it.""" self.world.unregister(self) self.undraw() def redraw(self): """Undraws and then redraws the animal.""" if self.world.exists: self.undraw() self.draw() def polar(self, x, y, r, theta): """Converts polar coordinates to cartesian. Args: x, y: location of the origin r: radius theta: angle in degrees Returns: tuple of x, y coordinates """ rad = theta * math.pi/180 s = math.sin(rad) c = math.cos(rad) return [ x + r * c, y + r * s ] def wait_for_user(): """Invokes wait_for_user on the most recent World.""" World.current_world.wait_for_user() if __name__ == '__main__': # make a generic world world = World() # create a canvas and put a text item on it ca = world.ca() ca.text([0,0], 'hello') # wait for the user wait_for_user()

AltStyle によって変換されたページ (->オリジナル) /