##April 2010 ##CSC323 ##The GUI for filling and editing a board ###Used to be called GraphicsUI from tkinter import * from ButtonCreator import * import Menu import Save #Code to import ../model/LocalWorld.py, listener import sys sys.path.append('../model') import Species, SpeciesDatabase import time import LocalWorld def run(width, height, defSpec=None, defCol=None, lworld=None, event = None): """This is where species are set in the grid, and the main UI for game play.""" root = Tk() if lworld == None: lworld = LocalWorld.LocalWorld(defSpec, width, height) screen = Board(root, width, height, defSpec, lworld) root.mainloop() class Board: def __init__(self, parent, w, h, default, world): """We are created by the controller, who contains a SpeciesDatabase and Listens to the world, also passed to us""" self.world = world self.world.addListener(self) self.parent = parent self.width = w self.height = h self.defaultSpecies = default self.defaultSpeciesColor = default.color self.specdb = SpeciesDatabase.getInstance() self.round = 0 self.roundText = StringVar() self.scale = 1 self.sqdim = 25 self.createBase() def createBase(self): """Create the two basic containers for BoardScreen: the Toolbars and the Board""" self.toolbar = Frame(self.parent, width = 15, bg = "tan") self.toolbar.pack(side = LEFT, fill = Y, expand = NO) """Create the sub Frames for Toolbar""" self.menu(self.toolbar) self.speciesSelector(self.toolbar) self.stepControls(self.toolbar) """Create the sub Frames for Board""" self.createBoard(self.parent) def menu(self, parent): """Creates the menu toolbar in the toolbar frame""" self.menuFrame= Frame(parent) self.menuFrame.pack(pady = 15) self.mainMenuButton = ButtonCreator(self.menuFrame, "Main Menu", \ self.spawnMenu, width = 9) self.quitButton = \ ButtonCreator(self.menuFrame, "Quit", self.destroyParent, width = 9) ButtonCreator(self.menuFrame, "Zoom in", self.zoomIn, width = 9) ButtonCreator(self.menuFrame, "Zoom out", self.zoomOut, width = 9) ButtonCreator(self.menuFrame, "Save", self.save, width =9) ButtonCreator(self.menuFrame, "Set All", self.setAll, width= 9) def speciesSelector(self, parent): """Creates the Species Selection Box""" specnames = self.specdb.allSpeciesNames() self.prev_color = StringVar(value=self.specdb.getColorfromName(\ self.defaultSpecies.kind)) """Frame to hold buttons for species types""" self.speciesButtonsToolbar = Frame(parent) self.speciesButtonsToolbar.pack(pady=20) for name in specnames: color = self.specdb.getColorfromName(name) b = ButtonCreator(self.speciesButtonsToolbar, name, width = 10,\ radio = 1, radiovar = self.prev_color,\ radioval = color, \ background = color) def stepControls(self, parent): """Create the step contols: play, pause, step, etc""" self.stepFrame = Frame(parent, height = 50) self.stepFrame.pack(side=BOTTOM, fill = X) """Play status: Whether play or stop has been clicked. 0 = stop, 1 = play""" self.play_status = IntVar(value=0) ButtonCreator(self.stepFrame, "Play", width = 6, packOption = TOP,\ pady = 5, callback = self.playClick) ButtonCreator(self.stepFrame, "Stop", width = 6, packOption = TOP,\ pady = 5, callback = self.stopClick) ButtonCreator(self.stepFrame, "Step", width = 6, packOption = TOP,\ pady = 5, callback = self.stepClick) Label(self.stepFrame, text = "Round: ", pady = 20).pack(side = LEFT) Label(self.stepFrame, textvariable = self.roundText, padx = 5).pack(side = RIGHT) self.roundText.set(str(self.round)) def createBoard(self, parent): """Creates the board that represents the local world""" self.boardFrame = Frame(parent) self.boardFrame.grid_rowconfigure(0, weight=1) self.boardFrame.grid_columnconfigure(0, weight=1) self.xscrollbar = Scrollbar(self.boardFrame, orient=HORIZONTAL) self.xscrollbar.grid(row=1, column=0, sticky=E+W) self.yscrollbar = Scrollbar(self.boardFrame) self.yscrollbar.grid(row=0, column=1, sticky=N+S) """The Canvas holds the colored squares that represent the board.""" self.cellCanvas = Canvas(self.boardFrame, bg = "white",\ width=self.width*self.sqdim, height=self.height*self.sqdim,\ xscrollcommand=self.xscrollbar.set,\ yscrollcommand=self.yscrollbar.set,\ scrollregion = (0,0, self.width*self.sqdim,\ self.height*self.sqdim)) self.xscrollbar.config(command=self.cellCanvas.xview) self.yscrollbar.config(command=self.cellCanvas.yview) #self.cellCanvas.pack(side = TOP, fill = BOTH, expand = YES) self.cellCanvas.grid(row=0, column=0, sticky=N+S+E+W) self.cellCanvas.config(scrollregion=self.cellCanvas.bbox(ALL)) self.boardFrame.pack(side = TOP, fill = BOTH) self.createCells() self.cellCanvas.bind("", self.canvasClick) self.cellCanvas.bind("", self.canvasClick) def createCells(self): """Creates a grid of colored squares to fill the canvas.""" for j in range(self.height): for i in range(self.width): """The create_rectangle() method takes an upper left corner and a bottom right corner. These corners are calculated based on where in the grid it should be & an assumed square size of 25""" self.cellCanvas.create_rectangle(i*self.sqdim, j*self.sqdim,(i+1)*self.sqdim, (j+1)*self.sqdim,\ fill=self.world.getSpecies(i,j).color) def canvasClick(self, event): """When the mouse is clicked on the canvas the closest grid square is set to the selected color.""" """This awesome line calls itemconfigure on the nearest square in cellCanvas. Find_closest returns a tuple of some info, we want the objectID. The fill color is based on what button has been selected in speciesSelector""" if self.play_status.get() == 0: newCanvasX = self.sqdim*self.width*self.xscrollbar.get()[0]*self.scale + event.x newCanvasY = self.sqdim*self.height*self.yscrollbar.get()[0]*self.scale + event.y newWorldX = int(newCanvasX/(self.scale*self.sqdim)) newWorldY = int(newCanvasY/(self.scale*self.sqdim)) # print(str(int(newX/(self.scale*25))) + " " + str(int(newY/(self.scale*25)))) '''only change the canvas if the representation of the world changes''' if self.world.setSpecies(newWorldX, newWorldY,\ self.specdb.getSpeciesfromColor(self.prev_color.get())): self.cellCanvas.itemconfigure(self.cellCanvas.find_closest(newCanvasX, newCanvasY), \ fill = self.prev_color.get()) def setAll(self, event): self.cellCanvas.itemconfigure(ALL, fill=self.prev_color.get()) for j in range(self.height): for i in range(self.width): self.world.setSpecies(i, j, self.specdb.getSpeciesfromColor(self.prev_color.get())) def zoomIn(self, event): """Self explanatory: zooms in the board""" self.cellCanvas.scale(ALL, 0, 0, 1.25, 1.25) self.scale *= 1.25 print (str(self.scale)) self.cellCanvas.configure(scrollregion=(0,0, self.width*self.sqdim*self.scale, self.height*self.sqdim*self.scale)) def zoomOut(self,event): """Self explanatory: zooms out when looking at the board""" self.cellCanvas.scale(ALL, 0, 0, .8, .8) self.scale *= .8 print (str(self.scale)) self.cellCanvas.configure(scrollregion=(0,0, self.width*self.sqdim*self.scale, self.height*self.sqdim*self.scale)) def save(self, event =None): """Calls a save menu so the user can enter a filename to save the board and species database to""" Save.run(self.world) def stepClick(self, event): """Called when the step button is clicked. Steps the localworld.""" """If stop is selected, and not play, we step""" if self.play_status.get() == 0: self.world.step() #self.round += 1 self.roundText.set(str(self.round)) else: print("Cannot step: play is currently selected") def stopClick(self,event): """Called when stop button is clicked. Stops an in-progress game""" self.play_status.set(0) def playClick(self, event): """Called when play button is clicked. Calls the play command""" self.play_status.set(1) self.play(event) def play(self,event): """Steps the world multiple times. Stops after certain # of rounds""" while self.play_status.get() == 1: """While the count is < max and stop button not pressed""" self.world.step() """Update gets changes from the GUI- after a sleep command to allow for humans to visible see changes in the board between rounds""" if self.width < 30 and self.height < 30: time.sleep(.1) self.parent.update() #self.round += 1 self.roundText.set(str(self.round)) """After done playing due to timeout, sets status to 'nonplaying'""" self.play_status.set(0) def destroyParent(self, event= None): """Destroys the frame, in theory""" self.parent.destroy() def spawnMenu(self, event = None): """Loads the Main Menu and destroys this window""" self.destroyParent(event) Menu.run(event) def update(self, changes): """Told to update by localworld, given list of changes, applies changes""" if (changes == None): return #print(self.world) else: self.round+=1 for c in changes: #print(str(c.coords[0]) + " " + str(c.coords[1])) self.cellCanvas.itemconfigure(self.cellCanvas.find_closest(\ c.coords[0]*self.sqdim*(self.scale), (self.scale)*c.coords[1]*self.sqdim),\ fill = c.species.color)