# L-18 MCS 275 Mon 22 Feb 2010 : hanoi2gui.py # The GUI to animate the tower of Hanoi uses the inverted iterative version # of the original recursive code in hanoi2.py. from Tkinter import * from hanoi2 import * class Hanoi2GUI(): """ GUI to show the towers of Hanoi. """ def __init__(self,wdw,n): """ Defines a canvas and buttons to animate the towers of Hanoi. """ wdw.title("Towers of Hanoi") # top buttons self.b1 = Button(wdw,text='start over',command=self.StartOver) self.b1.grid(row=0,column=0,sticky=W+E) self.b2 = Button(wdw,text='next move',command=self.NextMove) self.b2.grid(row=0,column=1,sticky=W+E) # the canvas self.cv = Canvas(wdw,width=400,height=200,bg='white') self.cv.grid(row=1,column=0,columnspan=2) # buttons at bottom for animation self.b3 = Button(wdw,text='start animation',command=self.Start) self.b3.grid(row=2,column=0,sticky=W+E) self.b4 = Button(wdw,text='stop animation',command=self.Stop) self.b4.grid(row=2,column=1,sticky=W+E) # entry widget to display status of piles self.e = Entry(wdw) self.e.grid(row=3,column=0,columnspan=2,sticky=W+E) # data attributes self.n = n self.A = ('A',range(1,n+1)) self.B = ('B',[]) self.C = ('C',[]) self.S = [(n,'A','B','C',0)] d = Piles2String('at start:',self.A,self.B,self.C) self.e.insert(INSERT,d) self.m = 0 self.FirstPegs() self.go = False def FirstPegs(self): """ Puts the first piles on canvas. """ self.cv.create_rectangle(73,20,77,170,fill='green',tags='Apile') self.cv.create_text(75,180,text='A') self.FirstPile(75) self.cv.create_rectangle(198,20,202,170,fill='green',tags='Bpile') self.cv.create_text(200,180,text='B') self.cv.create_rectangle(323,20,327,170,fill='green',tags='Cpile') self.cv.create_text(325,180,text='C') def FirstPile(self,middle): """ Puts the first pile on canvas, centered at x = middle. The bottom disk has width 100 pixels, the top disk is 20 pixels wide. The thickness of disks equals 200/self.n. """ x0 = middle - 50; x1 = middle + 50 y0 = 170 - 100/self.n; y1 = 170 d = 40/self.n self.cv.create_rectangle(x0,y0,x1,y1,fill='red',tags='disk') for i in range(1,self.n): x0 = x0 + d; x1 = x1 - d y1 = y0; y0 = y0 - 100/self.n self.cv.create_rectangle(x0,y0,x1,y1,fill='red',tags='disk') def PlacePile(self,middle,disks): """ Places the piles on canvas as defined in the list disks. """ nd = len(disks) y0 = 170 - 100/self.n; y1 = 170 d = 40/self.n for i in range(nd-1,-1,-1): disk = disks[i] x0 = middle - 50 + (self.n - disk)*d x1 = middle + 50 - (self.n - disk)*d self.cv.create_rectangle(x0,y0,x1,y1,fill='red',tags='disk') y1 = y0; y0 = y0 - 100/self.n def MovePiles(self): """ Updates the canvas with the new position of the piles. """ self.PlacePile(75,self.A[1]) self.PlacePile(200,self.B[1]) self.PlacePile(325,self.C[1]) def ClearPiles(self): """ Removes all disks from canvas using the piles. """ for i in range(0,self.n): self.cv.delete('disk') def StartOver(self): """ Brings the disks in the initial position. """ self.A = ('A',range(1,self.n+1)) self.B = ('B',[]) self.C = ('C',[]) self.S = [(self.n,'A','B','C',0)] d = Piles2String('at start:',self.A,self.B,self.C) self.e.delete(0,END) self.e.insert(INSERT,d) self.m = 0 self.ClearPiles() self.FirstPile(75) def NextMove(self): """ Executes the next move of a disk. """ self.m = self.m + 1 move = Get_Next_Move(self.S,self.A,self.B,self.C) p = 'after move %d :' % self.m d = Piles2String(p,self.A,self.B,self.C) self.e.delete(0,END) self.e.insert(INSERT,d) self.ClearPiles() self.MovePiles() self.cv.update() def Start(self): """ Starts the animation. """ self.go = True while self.go: self.NextMove() self.cv.after(600) if self.m >= 2**self.n: self.go = False def Stop(self): """ Stops the animation. """ self.go = False def main(): """ Prompts the user for the number of disks and launches the GUI. """ n = input('Give the number of disks : ') top = Tk() show = Hanoi2GUI(top,n) top.mainloop() if __name__ == "__main__": main()