_tkinter.TclError:无效的命令名称“ .4302957584”


问题内容

关闭python 3程序时,控制台中出现一个奇怪的异常。

Python 3代码:

from tkinter import *
from random import randint

# Return a random color string in the form of #RRGGBB
def getRandomColor():
    color = "#"
    for j in range(6):
        color += toHexChar(randint(0, 15)) # Add a random digit
    return color

# Convert an integer to a single hex digit in a character
def toHexChar(hexValue):
    if 0 <= hexValue <= 9:
        return chr(hexValue + ord('0'))
    else: # 10 <= hexValue <= 15
        return chr(hexValue - 10 + ord('A'))

# Define a Ball class
class Ball:
    def __init__(self):
        self.x = 0 # Starting center position
        self.y = 0
        self.dx = 2 # Move right by default
        self.dy  = 2 # Move down by default
        self.radius = 3
        self.color = getRandomColor()

class BounceBalls:
    def __init__(self):
        self.ballList = [] # Create a list for balls

        window = Tk()
        window.title("Bouncing Balls")

        ### Create Canvas ###
        self.width = 350
        self.height = 150
        self.canvas = Canvas(window, bg = "white", width = self.width, height = self.height)
        self.canvas.pack()


        ### Create Buttons ###
        frame = Frame(window)
        frame.pack()

        btStop = Button(frame, text = "Stop", command = self.stop)
        btStop.pack(side = LEFT)

        btResume = Button(frame, text = "Resume", command = self.resume)
        btResume.pack(side = LEFT)

        btAdd = Button(frame, text = "Add", command = self.add)
        btAdd.pack(side = LEFT)

        btRemove = Button(frame, text = "Remove", command = self.remove)
        btRemove.pack(side = LEFT)

        self.sleepTime = 20
        self.isStopped = False
        self.animate()

        window.mainloop()

    def stop(self): # Stop animation
        self.isStopped = True

    def resume(self):
        self.isStopped = False
        self.animate()

    def add(self): # Add a new ball
        self.ballList.append(Ball())

    def remove(self):
        self.ballList.pop()

    def animate(self):
        while not self.isStopped:
            self.canvas.after(self.sleepTime)
            self.canvas.update()
            self.canvas.delete("ball")

            for ball in self.ballList:
                self.redisplayBall(ball)

    def redisplayBall(self, ball):
        if ball.x > self.width or ball.x < 0:
            ball.dx = -ball.dx

        if ball.y > self.height or ball.y < 0:
            ball.dy = -ball.dy

        ball.x += ball.dx
        ball.y += ball.dy
        self.canvas.create_oval(ball.x - ball.radius, ball.y - ball.radius, \
                                ball.x + ball.radius, ball.y + ball.radius, \
                                fill = ball.color, tags = "ball")

BounceBalls()

这是完整的回溯:

/Library/Frameworks/Python.framework/Versions/3.3/bin/python3 "/Users/narek_a/Dropbox/Python/PycharmProjects/Introduction to Programming/Chapter 10.py"
Traceback (most recent call last):
  File "/Users/narek_a/Dropbox/Python/PycharmProjects/Introduction to Programming/Chapter 10.py", line 446, in <module>
    BounceBalls()
  File "/Users/narek_a/Dropbox/Python/PycharmProjects/Introduction to Programming/Chapter 10.py", line 407, in __init__
    self.animate()
  File "/Users/narek_a/Dropbox/Python/PycharmProjects/Introduction to Programming/Chapter 10.py", line 428, in animate
    self.canvas.delete("ball")
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/tkinter/__init__.py", line 2344, in delete
    self.tk.call((self._w, 'delete') + args)
_tkinter.TclError: invalid command name ".4302957584"

Process finished with exit code 1

为什么会引起这些异常?


问题答案:

window.mainloop如果不按下stop按钮,将永远不会执行主应用程序循环。这是您问题的根源。重写动画循环:

def __init__(self):
    ...
    self.sleepTime = 20
    self.isStopped = False
    self.window = window
    self.window.after(self.sleepTime, self.animate)
    window.mainloop()
    ...

def animate(self):
    if not self.isStopped:
        self.canvas.update()
        self.canvas.delete("ball")

        for ball in self.ballList:
            self.redisplayBall(ball)
        self.window.after(self.sleepTime, self.animate)