调用root.destroy()

时间:2015-10-21 12:06:05

标签: python tkinter tkinter-canvas

我正在使用Python 3.X学习tkinter。我正在编写一个简单的程序,它将获得一个或多个球(tkinter椭圆形)在矩形球场周围弹跳(tkinter根窗口上画有画布和矩形)。

我希望能够通过按q键干净地终止程序,并设法将密钥绑定到根并在按下某个键时激活回调函数,然后调用{{1 }}

但是,当我这样做时,我仍然收到root.destroy()形式的错误。这真让我抓狂。我做错了什么?

_tkinter.TclError: invalid command name ".140625086752360"

我已经包含了完整性的完整列表,但我很确定问题出在from tkinter import * import time import numpy class Ball: def bates(): """ Generator for the sequential index number used in order to identify the various balls. """ k = 0 while True: yield k k += 1 index = bates() def __init__(self, parent, x, y, v=0.0, angle=0.0, accel=0.0, radius=10, border=2): self.parent = parent # The parent Canvas widget self.index = next(Ball.index) # Fortunately, I have all my feathers individually numbered, for just such an eventuality self.x = x # X-coordinate (-1.0 .. 1.0) self.y = y # Y-coordinate (-1.0 .. 1.0) self.radius = radius # Radius (0.0 .. 1.0) self.v = v # Velocity self.theta = angle # Angle self.accel = accel # Acceleration per tick self.border = border # Border thickness (integer) self.widget = self.parent.canvas.create_oval( self.px() - self.pr(), self.py() - self.pr(), self.px() + self.pr(), self.py() + self.pr(), fill = "red", width=self.border, outline="black") def __repr__(self): return "[{}] x={:.4f} y={:.4f} v={:.4f} a={:.4f} r={:.4f} t={}, px={} py={} pr={}".format( self.index, self.x, self.y, self.v, self.theta, self.radius, self.border, self.px(), self.py(), self.pr()) def pr(self): """ Converts a radius from the range 0.0 .. 1.0 to window coordinates based on the width and height of the window """ assert self.radius > 0.0 and self.radius <= 1.0 return int(min(self.parent.height, self.parent.width)*self.radius/2.0) def px(self): """ Converts an X-coordinate in the range -1.0 .. +1.0 to a position within the window based on its width """ assert self.x >= -1.0 and self.x <= 1.0 return int((1.0 + self.x) * self.parent.width / 2.0 + self.parent.border) def py(self): """ Converts a Y-coordinate in the range -1.0 .. +1.0 to a position within the window based on its height """ assert self.y >= -1.0 and self.y <= 1.0 return int((1.0 - self.y) * self.parent.height / 2.0 + self.parent.border) def Move(self, x, y): """ Moves ball to absolute position (x, y) where x and y are both -1.0 .. 1.0 """ oldx = self.px() oldy = self.py() self.x = x self.y = y deltax = self.px() - oldx deltay = self.py() - oldy if oldx != 0 or oldy != 0: self.parent.canvas.move(self.widget, deltax, deltay) def HandleWallCollision(self): """ Detects if a ball collides with the wall of the rectangular Court. """ pass class Court: """ A 2D rectangular enclosure containing a centred, rectagular grid of balls (instances of the Ball class). """ def __init__(self, width=1000, # Width of the canvas in pixels height=750, # Height of the canvas in pixels border=5, # Width of the border around the canvas in pixels rows=1, # Number of rows of balls cols=1, # Number of columns of balls radius=0.05, # Ball radius ballborder=1, # Width of the border around the balls in pixels cycles=1000, # Number of animation cycles tick=0.01): # Animation tick length (sec) self.root = Tk() self.height = height self.width = width self.border = border self.cycles = cycles self.tick = tick self.canvas = Canvas(self.root, width=width+2*border, height=height+2*border) self.rectangle = self.canvas.create_rectangle(border, border, width+border, height+border, outline="black", fill="white", width=border) self.root.bind('<Key>', self.key) self.CreateGrid(rows, cols, radius, ballborder) self.canvas.pack() self.afterid = self.root.after(0, self.Animate) self.root.mainloop() def __repr__(self): s = "width={} height={} border={} balls={}\n".format(self.width, self.height, self.border, len(self.balls)) for b in self.balls: s += "> {}\n".format(b) return s def key(self, event): print("Got key '{}'".format(event.char)) if event.char == 'q': print("Bye!") self.root.after_cancel(self.afterid) self.root.destroy() def CreateGrid(self, rows, cols, radius, border): """ Creates a rectangular rows x cols grid of balls of the specified radius and border thickness """ self.balls = [] for r in range(1, rows+1): y = 1.0-2.0*r/(rows+1) for c in range(1, cols+1): x = 2.0*c/(cols+1) - 1.0 self.balls.append(Ball(self, x, y, 0.001, numpy.pi/6.0, 0.0, radius, border)) def Animate(self): """ Animates the movement of the various balls """ for c in range(self.cycles): for b in self.balls: b.v += b.accel b.Move(b.x + b.v * numpy.cos(b.theta), b.y + b.v * numpy.sin(b.theta)) self.canvas.update() time.sleep(self.tick) self.root.destroy() 类。我认为这是某种回调或类似的射击,但我似乎是在试图修复它的墙上敲我的头。

1 个答案:

答案 0 :(得分:4)

你实际上有两个主循环。在Court.__init__方法中,您使用after启动Animate方法,然后启动Tk主循环,该主循环将处理事件,直到您销毁主Tk窗口。

然而,Animate方法基本上通过调用update处理事件然后time.sleep来复制此主循环,以浪费一些时间并重复此操作。当您处理按键并终止窗口时,Animate方法仍在运行,并尝试更新不再存在的画布。

处理此问题的正确方法是重写Animate方法以执行单轮移动球,然后使用Animate安排另一个after来电并提供必要的延迟作为after参数。这样,事件系统将以正确的间隔调用您的动画功能,同时仍然会立即处理所有其他窗口系统事件。