#!/usr/bin/env python from Tkinter import * from PIL import Image, ImageTk from tkFileDialog import * from PathGraph import * from PathPathfinding import * import sys import time import os # A class used to visualize planning algorithms, as well as to display them. # Intelligently adapts itself, so that any number of implemented planning algorithms may be displayed and ran. class PathMapGui: #Initialize the gui. def __init__(self, graph = None, image = None): self.tkroot = tk = Tk() # the root of the bunch. self.tkroot.title("Planner") tk.resizable(width=FALSE, height=FALSE) self.graph = graph self.im = image self.pathObjects = [] self.pathfindings = [] self.pathmenuRadio = [] self.initGui() if graph != None and image != None: self.createGraph() #start the main loop of tk. def run(self): self.tkroot.mainloop() # Create all the elements of the gui. def initGui(self): self.initMenu() self.initMap() self.initControls() self.initSidebar() self.initEvents() # Initialize the top menu. def initMenu(self): menubar = Menu(self.tkroot) # Dummy function for buttons def hello(): print 'hullo' filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label="Open", command=self.loadMap) filemenu.add_command(label="Reload Map", command=self.createGraph) filemenu.add_separator() filemenu.add_command(label="Exit", command=self.tkroot.quit) menubar.add_cascade(label="File", menu=filemenu) self.tkroot.config(menu=menubar) # Initialize the main map used. def initMap(self): self.map = Canvas(self.tkroot, width=20, height=20) self.map.grid(row=0) self.startCircle, self.endCircle = None, None # Initialize the controls used at the bottom. def initControls(self): self.pathFrame = Frame(self.tkroot, width=20, height=20) self.pathFrame.grid(row=1) Label(self.pathFrame, text='Start x:').grid(row=0) Label(self.pathFrame, text='Start y:').grid(row=1) self.esx = IntVar() Label(self.pathFrame, textvariable = self.esx, width=4).grid(row=0,column=1) self.esy = IntVar() Label(self.pathFrame, textvariable = self.esy, width=4).grid(row=1,column=1) Label(self.pathFrame, text='Goal x:').grid(row=0,column=2) Label(self.pathFrame, text='Goal y:').grid(row=1,column=2) self.egx = IntVar() Label(self.pathFrame, textvariable = self.egx, width=4).grid(row=0,column=3) self.egy = IntVar() Label(self.pathFrame, textvariable = self.egy, width=4).grid(row=1,column=3) b = Button(self.pathFrame, text="Find Path", command=self.pathfind) b.grid(row=0,column=4) b = Button(self.pathFrame, text="Smooth Path", command=self.pathsmooth) b.grid(row=1,column=4) self.planDraw = BooleanVar() Checkbutton(self.pathFrame, text="Animation", variable=self.planDraw, onvalue=True, offvalue=False).grid(row=2, column=2) # Initialize the sidebar that displays the current planning algorithm and information on it. # sidebar1 contains the planning algorithms, sidebar2 contains the information. def initSidebar(self): def hello(): print "hullo" self.sidebarMaster = Frame(self.tkroot, width=20, height=40) self.sidebarMaster.grid(row=0, column=1) self.sidebar1 = Frame(self.sidebarMaster, width=20, height=20) self.sidebar1.grid(row=0) Label(self.sidebar1, text='Planning Algorithms').grid(row=0) self.pathmenuRadioVal = IntVar() self.sidebar2 = Frame(self.sidebarMaster, width=20, height=20) self.sidebar2.grid(row=1) Label(self.sidebar2, text='Plan Information').grid(row=0) self.pathStatsLabels = [] # Initialize the event handling def initEvents(self): # Event handling for setting the start point and graphic. def setStart(event): if not self.graph or not (self.graph.nodeDict.get((event.x, event.y), None) or self.graph.nodeDict.get((event.x, event.y), None)): return elif self.startCircle != None: self.map.delete(self.startCircle) self.esx.set((event.x)) self.esy.set((event.y)) self.startCircle = self.map.create_rectangle(event.x-3,event.y-3,event.x+3,event.y+3,outline='purple',fill='blue') # Event handling for setting the goal point and graphic. def setGoal(event): if not self.graph or not (self.graph.nodeDict.get((event.x, event.y), None) or self.graph.nodeDict.get((event.x, event.y), None)): return elif self.endCircle != None: self.map.delete(self.endCircle) self.egx.set((event.x)) self.egy.set((event.y)) self.endCircle = self.map.create_rectangle(event.x-3,event.y-3,event.x+3,event.y+3,outline='green',fill='blue') self.map.bind('', setStart) self.map.bind('<1>', setStart) self.map.bind('', setGoal) self.map.bind('<3>', setGoal) # Create the mandatory elements to display and utilize a graph. def createGraph(self): if not (self.graph and self.im): print 'Map not loaded yet' return else: self.pathfindings = allPathfinding(self.graph) self.mapbg = ImageTk.PhotoImage(self.im) self.map.config(height=self.mapbg.height(), width=self.mapbg.width()) self.pathFrame.config(width=self.mapbg.width()) self.mapim = self.map.create_image((0,0), image=self.mapbg, anchor='nw') for radio in self.pathmenuRadio: radio.grid_forget() self.pathmenuRadio = [] self.esx.set(0);self.esy.set(0);self.egx.set(0);self.egy.set(0) gridval = 1 for i in range(len(self.pathfindings)): b = Radiobutton(self.sidebar1, text=self.pathfindings[i].label, variable=self.pathmenuRadioVal, value=i) b.grid(row=i+1) self.pathmenuRadio.append(b) self.pathfindings[i].SetGui(self) self.clearPathStats() # Load a map file. def loadMap(self): mapFile = askopenfilename(initialdir='../data') if os.path.exists(mapFile): self.graph, self.im = loadMapFile(mapFile) self.createGraph() else: print 'cannot find file' # Delete the gui objects used to display the last path, then displays a new path found. # If "Animation" is checked, displays the animation as specified by the planning algorithm. def pathfind(self): if self.pathObjects: for pathObject in self.pathObjects: if pathObject: for obj in pathObject: self.map.delete(obj) self.pathObjects = [] pathfind = self.pathfindings[self.pathmenuRadioVal.get()] color = 'red' pathfind.FindPath( self.graph.getNodeID((self.esx.get(), self.esy.get())), self.graph.getNodeID((self.egx.get(), self.egy.get())) ) pathfind.ClearDraws() path = pathfind.path self.pathObjects.append(self.displayPath(path, color)) self.updatePathStats() # Not currently used # Problem was how to get the path from function above into this one def pathsmooth(self): pathfind = self.pathfindings[self.pathmenuRadioVal.get()] pathfind.Smooth() smoothpath = pathfind.path self.pathObjects.append(self.displayPath(smoothpath, 'blue')) print "Now doing something" print "This button works!" self.updatePathStats() # Display a path between the start and the goal. def displayPath(self, path, color): if path: pathobjects = [] for node in path: (x,y) = self.graph.getNode(node).getCoordinates() #print "x= ", x, " y= ", y pathobjects.append(self.map.create_rectangle(x,y,x,y,outline=color)) (x0,y0) = self.graph.getNode(path[0]).getCoordinates() pathobjects.append(self.map.create_rectangle(x0-1,y0-1,x0+1,y0+1,outline='purple',fill=color)) (x1,y1) = self.graph.getNode(path[-1]).getCoordinates() pathobjects.append(self.map.create_rectangle(x1-1,y1-1,x1+1,y1+1,outline='green',fill=color)) return pathobjects else: return None # Delete and stop displaying all of the stats def clearPathStats(self): for i in self.pathStatsLabels: i.grid_forget() # Ask the current planning algorithm for stats, and display them. def updatePathStats(self): pathStats = self.pathfindings[self.pathmenuRadioVal.get()].GetStats() self.clearPathStats() for i in range(len(pathStats)): iden, data = pathStats[i] LabelStatID = Label(self.sidebar2, text = iden) LabelStatID.grid(row=i+1) LabelStatData = Label(self.sidebar2, text = data) LabelStatData.grid(row=i+1, column=1) self.pathStatsLabels.extend([LabelStatID ,LabelStatData]) # Run the gui! def main(): a = PathMapGui() a.run() # Run the gui! if __name__ == '__main__': main()