“tkinter.TclError:无效的命令名称"调用 root.destroy() 后出错

编程入门 行业动态 更新时间:2024-10-19 06:27:45
本文介绍了“tkinter.TclError:无效的命令名称"调用 root.destroy() 后出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

限时送ChatGPT账号..

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

I am in the process of learning tkinter on Python 3.X. I am writing a simple program which will get one or more balls (tkinter ovals) bouncing round a rectangular court (tkinter root window with a canvas and rectangle drawn on it).

我希望能够通过按 q 键干净地终止程序,并设法将键绑定到根并在按下键时触发回调函数,然后调用root.destroy().

I want to be able to terminate the program cleanly by pressing the q key, and have managed to bind the key to the root and fire the callback function when a key is pressed, which then calls root.destroy().

但是,当我这样做时,我仍然收到 _tkinter.TclError: invalid command name ".140625086752360" 形式的错误.这真让我抓狂.我做错了什么?

However, I'm still getting errors of the form _tkinter.TclError: invalid command name ".140625086752360" when I do so. This is driving me crazy. What am I doing wrong?

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()

为了完整性,我已经包含了完整列表,但我相当确定问题出在 Court 类中.我认为这是某种回调或类似的触发,但我似乎在试图修复它.

I've included the full listing for completeness, but I'm fairly sure that the problem lies in the Court class. I presume it's some sort of callback or similar firing but I seem to be beating my head against a wall trying to fix it.

推荐答案

您已经有效地获得了两个主循环.在您的 Court.__init__ 方法中,您使用 after 启动 Animate 方法,然后启动将处理事件的 Tk 主循环,直到您销毁主Tk 窗口.

You have effectively got two mainloops. In your Court.__init__ method you use after to start the Animate method and then start the Tk mainloop which will process events until you destroy the main Tk window.

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

However the Animate method basically replicates this mainloop by calling update to process events then time.sleep to waste some time and repeating this. When you handle the keypress and terminate your window, the Animate method is still running and attempts to update the canvas which no longer exists.

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

The correct way to handle this is to rewrite the Animate method to perform a single round of moving the balls and then schedule another call of Animate using after and provide the necessary delay as the after parameter. This way the event system will call your animation function at the correct intervals while still processing all other window system events promptly.

这篇关于“tkinter.TclError:无效的命令名称"调用 root.destroy() 后出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

更多推荐

[db:关键词]

本文发布于:2023-04-30 06:41:44,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1390646.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:命令   名称   TclError   tkinter   root

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!