12

关闭 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

为什么会出现这些异常?

4

2 回答 2

10

当您退出程序时,窗口将被销毁。这种破坏发生在事件循环注意到应用程序退出之后。您构建代码的方式,当您调用self.update(). 在那次调用之后,并且在小部件被销毁后,您正在调用self.canvas.delete("ball"). 由于小部件在前面的语句中被销毁,您会收到错误消息。

错误如此神秘的原因是 Tkinter 只是一个 tcl/tk 解释器的包装器。Tk 小部件以点后跟一些字符命名,小部件名称也是 tcl 命令。当你调用画布的delete方法时,Tkinter 将画布引用转换为内部 tk 小部件名称/tcl 命令。由于小部件已被销毁,tk 不会将其识别为已知命令并引发错误。

该解决方案将要求您重做动画逻辑。你不应该有自己的动画循环。相反,您需要使用 Tkinter 事件循环 ( mainloop()) 作为动画循环。该站点上有一些示例向您展示如何操作(例如:https ://stackoverflow.com/a/11505034/7432 )

于 2013-04-17T13:16:05.100 回答
5

如果不按下按钮,主应用程序循环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)
于 2013-04-17T12:57:55.483 回答