[Tutor] request to run program on a Linux machine
Gregor Lingl
glingl at aon.at
Mon Feb 16 12:52:29 EST 2004
Hi!
At the moment I unfortunately don't have a computer running
Linux at hand, so ...
... is there anybody out there using a machine running
Python 2.3 with Tkinter under Linux, who is willing
to run the attached program and give me feedback concerning
two questions:
(1) Does the program run smoothly
(2) Does it terminate without error-messages (TclError)
when the window is closed while the animation is running
(i. e. while a disc is moving).
This can be seen e.g. when you run it from within IDLE or
from the commandline of some console window ...
Many thanks in advance
Gregor
P.S. A second posting concerning this Hanoi program,
concerning some remarks on the problem mentioned above
will follow.
-------------- next part --------------
## Animated Tower-Of-Hanoi game with Tkinter GUI
## author: Gregor Lingl, Vienna, Austria
## email: glingl at aon.at
## date: 16. 2. 2004
from Tkinter import *
class Disc:
"""Movable Rectangle on a Tkinter Canvas"""
def __init__(self,cv,pos,length,height,colour):
"""creates disc on given Canvas cv at given pos-ition"""
x0, y0 = pos
x1, x2 = x0-length/2.0, x0+length/2.0
y1, y2 = y0-height, y0
self.cv = cv
self.item = cv.create_rectangle(x1,y1,x2,y2,
fill = "#%02x%02x%02x" % colour)
def move_to(self, x, y, speed):
"""moves bottom center of disc to position (x,y).
speed is intended to assume values from 1 to 10"""
x1,y1,x2,y2 = self.cv.coords(self.item)
x0, y0 = (x1 + x2)/2, y2
dx, dy = x-x0, y-y0
d = (dx**2+dy**2)**0.5
steps = int(d/(10*speed-5)) + 1
dx, dy = dx/steps, dy/steps
for i in range(steps):
self.cv.move(self.item,dx,dy)
self.cv.update()
self.cv.after(20)
class Tower(list):
"""Hanoi tower designed as a subclass of built-in type list"""
def __init__(self, x, y, h):
""" creates an empty tower.
(x,y) is floor-level position of tower,
h is height of the discs. """
self.x = x
self.y = y
self.h = h
def top(self):
return self.x, self.y - len(self)*self.h
class HanoiEngine:
"""Plays the Hanoi-game on a given canvas."""
def __init__(self, canvas, nrOfDiscs, speed, moveCntDisplay=None):
"""Sets Canvas to play on as well as default values for
number of discs and animation-speed.
moveCntDisplay is a function with 1 parameter, which communicates
the count of the actual move to the GUI containing the
Hanoi-engine-canvas."""
self.cv = canvas
self.nrOfDiscs = nrOfDiscs
self.speed = speed
self.moveDisplay = moveCntDisplay
self.running = False
self.moveCnt = 0
self.discs = []
self.towerA = Tower( 80, 190, 15)
self.towerB = Tower(220, 190, 15)
self.towerC = Tower(360, 190, 15)
self.reset()
def hanoi(self, n, src, dest, temp):
"""The classical recursive Towers-Of-Hanoi algorithm."""
if n > 0:
for x in self.hanoi(n-1, src, temp, dest): yield None
yield self.move(src, dest)
for x in self.hanoi(n-1, temp, dest, src): yield None
def move(self, src_tower, dest_tower):
"""moves uppermost disc of source tower to top of destination
tower."""
self.moveCnt += 1
self.moveDisplay(self.moveCnt)
disc = src_tower.pop()
x1, y1 = src_tower.top()
x2, y2 = dest_tower.top()
disc.move_to(x1,20, self.speed)
disc.move_to(x2,20, self.speed)
disc.move_to(x2,y2, self.speed)
dest_tower.append(disc)
def reset(self):
"""Setup of (a new) game."""
self.moveCnt = 0
self.moveDisplay(self.moveCnt)
while self.towerA: self.towerA.pop()
while self.towerB: self.towerB.pop()
while self.towerC: self.towerC.pop()
for s in self.discs:
self.cv.delete(s.item)
## Fancy colouring of discs: red ===> blue
if self.nrOfDiscs > 1:
colour_diff = 255 // (self.nrOfDiscs-1)
else:
colour_diff = 0
for i in range(self.nrOfDiscs): # setup towerA
length_diff = 100 // self.nrOfDiscs
length = 120 - i * length_diff
s = Disc( self.cv, self.towerA.top(), length, 13,
(255-i*colour_diff, 0, i*colour_diff))
self.discs.append(s)
self.towerA.append(s)
self.HG = self.hanoi(self.nrOfDiscs,
self.towerA, self.towerC, self.towerB)
def run(self):
"""runs game ;-)
returns True if game is over, else False"""
self.running = True
try:
while self.running:
result = self.step()
return result # True iff done
except StopIteration: # game over
return True
except TclError: # for silently terminating in case of window closing
return False
def step(self):
"""performs one single step of the game,
returns True if finished, else False"""
self.HG.next()
return 2**self.nrOfDiscs-1 == self.moveCnt
def stop(self):
""" ;-) """
self.running = False
class Hanoi:
"""GUI for animated towers-of-Hanoi-game with upto 10 discs:"""
def displayMove(self, move):
"""method to be passed to the Hanoi-engine as a callback
to report move-count"""
self.moveCntLbl.configure(text = "move:\n%d" % move)
def adjust_nr_of_discs(self, e):
"""callback function for nr-of-discs-scale-widget"""
self.hEngine.nrOfDiscs = self.discs.get()
self.reset()
def adjust_speed(self, e):
"""callback function for speeds-scale-widget"""
self.hEngine.speed = self.tempo.get()
def setState(self, STATE):
"""most simple representation of a finite state machine"""
self.state = STATE
try:
if STATE == "START":
self.discs.configure(state=NORMAL)
self.discs.configure(fg="black")
self.discsLbl.configure(fg="black")
self.resetBtn.configure(state=DISABLED)
self.startBtn.configure(text="start", state=NORMAL)
self.stepBtn.configure(state=NORMAL)
elif STATE == "RUNNING":
self.discs.configure(state=DISABLED)
self.discs.configure(fg="gray70")
self.discsLbl.configure(fg="gray70")
self.resetBtn.configure(state=DISABLED)
self.startBtn.configure(text="pause", state=NORMAL)
self.stepBtn.configure(state=DISABLED)
elif STATE == "PAUSE":
self.discs.configure(state=NORMAL)
self.discs.configure(fg="black")
self.discsLbl.configure(fg="black")
self.resetBtn.configure(state=NORMAL)
self.startBtn.configure(text="resume", state=NORMAL)
self.stepBtn.configure(state=NORMAL)
elif STATE == "DONE":
self.discs.configure(state=NORMAL)
self.discs.configure(fg="black")
self.discsLbl.configure(fg="black")
self.resetBtn.configure(state=NORMAL)
self.startBtn.configure(text="start", state=DISABLED)
self.stepBtn.configure(state=DISABLED)
elif STATE == "TIMEOUT":
self.discs.configure(state=DISABLED)
self.discs.configure(fg="gray70")
self.discsLbl.configure(fg="gray70")
self.resetBtn.configure(state=DISABLED)
self.startBtn.configure(state=DISABLED)
self.stepBtn.configure(state=DISABLED)
except TclError:
pass
def reset(self):
"""restores START state for a new game"""
self.hEngine.reset()
self.setState("START")
def start(self):
"""callback function for start button, which also serves as
pause button. Makes hEngine running until done or interrupted"""
if self.state in ["START", "PAUSE"]:
self.setState("RUNNING")
if self.hEngine.run():
self.setState("DONE")
else:
self.setState("PAUSE")
elif self.state == "RUNNING":
self.setState("TIMEOUT")
self.hEngine.stop()
def step(self):
"""callback function for step button.
makes hEngine perform a single step"""
self.setState("TIMEOUT")
if self.hEngine.step():
self.setState("DONE")
else:
self.setState("PAUSE")
def __init__(self, nrOfDiscs, speed):
"""builds GUI, constructs Hanoi-engine and puts it into START state
then launches mainloop()"""
root = Tk()
root.title("TOWERS OF HANOI")
#root.protocol("WM_DELETE_WINDOW",root.quit) #counter productive here!?
cv = Canvas(root,width=440,height=210, bg="gray90")
cv.pack()
fnt = ("Arial", 12, "bold")
attrFrame = Frame(root) #contains scales to adjust game's attributes
self.discsLbl = Label(attrFrame, width=7, height=2, font=fnt,
text="discs:\n")
self.discs = Scale(attrFrame, from_=1, to_=10, orient=HORIZONTAL,
font=fnt, length=75, showvalue=1, repeatinterval=10,
command=self.adjust_nr_of_discs)
self.discs.set(nrOfDiscs)
self.tempoLbl = Label(attrFrame, width=8, height=2, font=fnt,
text = " speed:\n")
self.tempo = Scale(attrFrame, from_=1, to_=10, orient=HORIZONTAL,
font=fnt, length=100, showvalue=1,repeatinterval=10,
command = self.adjust_speed)
self.tempo.set(speed)
self.moveCntLbl= Label(attrFrame, width=5, height=2, font=fnt,
padx=20, text=" move:\n0", anchor=CENTER)
for widget in ( self.discsLbl, self.discs, self.tempoLbl, self.tempo,
self.moveCntLbl ):
widget.pack(side=LEFT)
attrFrame.pack(side=TOP)
ctrlFrame = Frame(root) # contains Buttons to control the game
self.resetBtn = Button(ctrlFrame, width=11, text="reset", font=fnt,
state = DISABLED, padx=15, command = self.reset)
self.stepBtn = Button(ctrlFrame, width=11, text="step", font=fnt,
state = NORMAL, padx=15, command = self.step)
self.startBtn = Button(ctrlFrame, width=11, text="start", font=fnt,
state = NORMAL, padx=15, command = self.start)
for widget in self.resetBtn, self.stepBtn, self.startBtn:
widget.pack(side=LEFT)
ctrlFrame.pack(side=TOP)
# setup of the scene
peg1 = cv.create_rectangle( 75, 40, 85, 190, fill='darkgreen')
peg2 = cv.create_rectangle( 215, 40, 225, 190, fill='darkgreen')
peg3 = cv.create_rectangle( 355, 40, 365, 190, fill='darkgreen')
floor = cv.create_rectangle( 10, 191, 430, 200, fill='black')
self.hEngine = HanoiEngine(cv, nrOfDiscs, speed, self.displayMove)
self.state = "START"
root.mainloop()
if __name__ == "__main__":
Hanoi(4,5)
More information about the Tutor
mailing list