Initial Commit
When using tkinter in python, there seems to be some sutble conflict when using tk.update() and tk.mainloop() at the same time.
This commit is contained in:
parent
0bc3cd92de
commit
08ccb4c68a
215
hanoi.py
Normal file
215
hanoi.py
Normal file
@ -0,0 +1,215 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Sat Apr 25 21:18:50 2020
|
||||
|
||||
@author: cpan
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
import time
|
||||
|
||||
gbl_num_disks = 3
|
||||
GAP = 2
|
||||
CANVAS_W = 500
|
||||
CANVAS_H = 400
|
||||
gbl_speed = 0.001 # in seconds
|
||||
gbl_pegs = []
|
||||
gbl_pegstate = [[], [], []]
|
||||
gbl_disks = []
|
||||
gbl_tkroot = tk.Tk()
|
||||
gbl_canvas = None
|
||||
gbl_ready = True
|
||||
|
||||
|
||||
def setup_gui():
|
||||
global gbl_canvas
|
||||
|
||||
def get_ent(event):
|
||||
global gbl_num_disks
|
||||
|
||||
try:
|
||||
new_num = int(ent_disks.get())
|
||||
gbl_num_disks = new_num
|
||||
event.widget.tk_focusNext().focus()
|
||||
draw_init_setting()
|
||||
except Exception as err:
|
||||
ent_disks.delete(0, tk.END)
|
||||
ent_disks.insert(0, str(gbl_num_disks))
|
||||
print(repr(err))
|
||||
# print(gbl_num_disks)
|
||||
|
||||
# options row
|
||||
tk_row = tk.Frame(gbl_tkroot)
|
||||
tk.Label(tk_row, text="No. of Disks: ").pack(side=tk.LEFT)
|
||||
ent_disks = tk.Entry(tk_row, width=5)
|
||||
ent_disks.insert(0, str(gbl_num_disks))
|
||||
ent_disks.bind('<Return>', get_ent)
|
||||
ent_disks.bind('<FocusOut>', get_ent)
|
||||
ent_disks.pack(side=tk.LEFT, padx=5)
|
||||
btn_start = tk.Button(tk_row, text="Start", width=6)
|
||||
btn_start.bind('<ButtonRelease-1>', run_hanoi)
|
||||
btn_start.pack(side=tk.LEFT, padx=5)
|
||||
btn_reset = tk.Button(tk_row, text="Reset", width=6)
|
||||
btn_reset.bind('<ButtonRelease-1>', reset_hanoi)
|
||||
btn_reset.pack(side=tk.LEFT, padx=5)
|
||||
tk.Label(tk_row, text="Speed: ").pack(side=tk.LEFT)
|
||||
tk_var = tk.StringVar(gbl_tkroot)
|
||||
tk_var.set('normal')
|
||||
tk_opt_menu = tk.OptionMenu(tk_row, tk_var,
|
||||
*{'slow', 'normal', 'fastest'})
|
||||
tk_opt_menu.config(width = 6)
|
||||
tk_opt_menu.pack(side=tk.LEFT)
|
||||
tk_row.pack(side=tk.TOP, pady=5)
|
||||
|
||||
# animation canvas
|
||||
gbl_canvas = tk.Canvas(gbl_tkroot, width=CANVAS_W, height=CANVAS_H)
|
||||
gbl_canvas.pack()
|
||||
|
||||
def change_speed(*args):
|
||||
global gbl_speed
|
||||
speed_opt = {'slow': 0.002, 'normal': 0.001, 'fastest': 0}
|
||||
gbl_speed = speed_opt.get(tk_var.get())
|
||||
# print(gbl_speed)
|
||||
|
||||
tk_var.trace('w', change_speed)
|
||||
|
||||
|
||||
def run_hanoi(event):
|
||||
global gbl_ready
|
||||
|
||||
if gbl_ready:
|
||||
gbl_ready = False
|
||||
try:
|
||||
hanoi(gbl_num_disks-1, 0, 1, 2)
|
||||
except Exception as err:
|
||||
print(f'forced reset: {repr(err)}')
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
def reset_hanoi(event):
|
||||
draw_init_setting()
|
||||
|
||||
|
||||
def draw_init_setting():
|
||||
global gbl_pegs, gbl_disks, gbl_pegstate, gbl_ready
|
||||
gbl_pegs = []
|
||||
gbl_pegstate = [[], [], []]
|
||||
gbl_disks = []
|
||||
|
||||
gbl_canvas.delete('all')
|
||||
# draw the three pegs a, b, c
|
||||
peg_w = 10
|
||||
peg_h = CANVAS_H // 2
|
||||
peg_dist = CANVAS_W // 3
|
||||
x1, y1 = (peg_dist - peg_w) // 2, CANVAS_H // 3
|
||||
x2, y2 = x1 + peg_w, y1 + peg_h
|
||||
|
||||
for i in range(3):
|
||||
gbl_pegs.append(gbl_canvas.create_rectangle(x1, y1, x2, y2, fill = 'black'))
|
||||
x1 += peg_dist
|
||||
x2 += peg_dist
|
||||
gbl_tkroot.update_idletasks()
|
||||
gbl_tkroot.update()
|
||||
|
||||
# create disks on the first peg
|
||||
disk_h = peg_h // 16
|
||||
disk_maxw = peg_dist * 2 // 3
|
||||
disk_minw = 2 * peg_w
|
||||
x1, y1 = (peg_dist - disk_maxw) // 2, y2 - disk_h - GAP
|
||||
x2, y2 = x1 + disk_maxw, y1 + disk_h
|
||||
dx = (disk_maxw - disk_minw) // (2 * max(1, gbl_num_disks-1))
|
||||
dx = min(dx, 2 * peg_w)
|
||||
|
||||
for i in range(gbl_num_disks):
|
||||
gbl_disks.insert(0, gbl_canvas.create_rectangle(x1, y1, x2, y2, fill = 'red'))
|
||||
gbl_pegstate[0].append(gbl_num_disks - i - 1)
|
||||
x1 += dx
|
||||
x2 -= dx
|
||||
y1 -= disk_h + GAP
|
||||
y2 -= disk_h + GAP
|
||||
gbl_tkroot.update_idletasks()
|
||||
gbl_tkroot.update()
|
||||
|
||||
gbl_ready = True
|
||||
|
||||
def move_disk(i, a, b):
|
||||
global gbl_pegstate, gbl_canvas
|
||||
disk_num = gbl_pegstate[a].pop()
|
||||
if disk_num != i:
|
||||
raise RuntimeError(f'Trying to move disk piece #{i} from peg {a}\
|
||||
to peg {b} that does not exist.')
|
||||
disk = gbl_disks[i]
|
||||
|
||||
# Lift the disk above peg a
|
||||
px1, py1, px2, py2 = gbl_canvas.bbox(gbl_pegs[a])
|
||||
while True:
|
||||
x1, y1, x2, y2 = gbl_canvas.bbox(disk)
|
||||
if y2 < py1:
|
||||
break
|
||||
gbl_canvas.move(disk, 0, -1)
|
||||
gbl_tkroot.update_idletasks()
|
||||
gbl_tkroot.update()
|
||||
time.sleep(gbl_speed)
|
||||
|
||||
# Move to peg b
|
||||
px1, py1, px2, py2 = gbl_canvas.bbox(gbl_pegs[b])
|
||||
new_center = (px1 + px2) // 2
|
||||
while True:
|
||||
x1, y1, x2, y2 = gbl_canvas.bbox(disk)
|
||||
center = (x1 + x2) // 2
|
||||
if center == new_center:
|
||||
break
|
||||
if center > new_center:
|
||||
gbl_canvas.move(disk, -1, 0)
|
||||
else:
|
||||
gbl_canvas.move(disk, 1, 0)
|
||||
gbl_tkroot.update_idletasks()
|
||||
gbl_tkroot.update()
|
||||
time.sleep(0.7*gbl_speed) # faster
|
||||
|
||||
# Drop down
|
||||
disk_h = y2 - y1
|
||||
new_bottom = py2 - disk_h * len(gbl_pegstate[b]) - GAP
|
||||
while True:
|
||||
x1, y1, x2, y2 = gbl_canvas.bbox(disk)
|
||||
if y2 >= new_bottom:
|
||||
break
|
||||
gbl_canvas.move(disk, 0, 1)
|
||||
gbl_tkroot.update_idletasks()
|
||||
gbl_tkroot.update()
|
||||
time.sleep(gbl_speed)
|
||||
|
||||
gbl_pegstate[b].append(i)
|
||||
|
||||
|
||||
def hanoi(n, a, b, c):
|
||||
'''
|
||||
Hanoi algorithm: move n pieces from a to c, using b as buffer.
|
||||
For each move, call animate() to show the move
|
||||
'''
|
||||
|
||||
if n < 0:
|
||||
return
|
||||
hanoi(n-1, a, c, b)
|
||||
move_disk(n, a, c) # move nth piece from peg a to peg c
|
||||
hanoi(n-1, b, a, c)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
setup_gui()
|
||||
draw_init_setting()
|
||||
|
||||
# input("Press Enter to continue")
|
||||
|
||||
# try:
|
||||
# hanoi(gbl_num_disks-1, 0, 1, 2)
|
||||
# except Exception as err:
|
||||
# print("aborted: " + repr(err))
|
||||
|
||||
gbl_tkroot.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user