在Tkinter画布中移动球体

8

这是一个非常基础的程序,我想制作两个移动的球,但只有一个球实际上在移动。

我也尝试了一些变化,但无法让第二个球运动;另一个相关的问题是,一些人使用move(object)方法来实现这一点,而其他人则使用delete(object)然后重新绘制它。我应该使用哪一个,为什么?

这是我的代码,只动画/移动了一个球:

from Tkinter import *

class Ball:
    def __init__(self, canvas, x1, y1, x2, y2):
    self.x1 = x1
    self.y1 = y1
    self.x2 = x2
    self.y2 = y2
    self.canvas = canvas
    self.ball = canvas.create_oval(self.x1, self.y1, self.x2, self.y2, fill="red")

    def move_ball(self):
        while True:
            self.canvas.move(self.ball, 2, 1)
            self.canvas.after(20)
            self.canvas.update()

# initialize root Window and canvas
root = Tk()
root.title("Balls")
root.resizable(False,False)
canvas = Canvas(root, width = 300, height = 300)
canvas.pack()

# create two ball objects and animate them
ball1 = Ball(canvas, 10, 10, 30, 30)
ball2 = Ball(canvas, 60, 60, 80, 80)

ball1.move_ball()
ball2.move_ball()

root.mainloop()

2
请修正缩进! - sundar nataraj
4个回答

15

你永远不应该在GUI程序中放置一个无限循环--已经有一个无限循环在运行。如果你想让你的球独立移动,只需移除循环,并在事件循环中的move_ball方法中添加一个新的调用。这样,你的球将一直移动,直到应用程序被销毁。

我稍微修改了你的程序,移除了无限循环,稍微减慢了动画速度,并且还使用随机值来确定它们的移动方向。所有这些变化都在move_ball方法中。

from Tkinter import *
from random import randint

class Ball:
    def __init__(self, canvas, x1, y1, x2, y2):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2
        self.canvas = canvas
        self.ball = canvas.create_oval(self.x1, self.y1, self.x2, self.y2, fill="red")

    def move_ball(self):
        deltax = randint(0,5)
        deltay = randint(0,5)
        self.canvas.move(self.ball, deltax, deltay)
        self.canvas.after(50, self.move_ball)

# initialize root Window and canvas
root = Tk()
root.title("Balls")
root.resizable(False,False)
canvas = Canvas(root, width = 300, height = 300)
canvas.pack()

# create two ball objects and animate them
ball1 = Ball(canvas, 10, 10, 30, 30)
ball2 = Ball(canvas, 60, 60, 80, 80)

ball1.move_ball()
ball2.move_ball()

root.mainloop()

啊 - 非常感谢Bryan,它完美地运行了(更重要的是我理解了)!我认为添加实际功能,如弹跳和加速度,不会很困难。 - dlohse
如果我不想要任何图形界面怎么办?Tk 是否包含一种虚拟小部件,或者其他带有 .after() 方法的东西,以便将循环放在其他循环旁边? - yota
@yota:如果你不想要任何图形界面,可以使用自己的循环。使用after是tkinter特有的解决方案,但是这些概念也可以应用于任何带有事件循环的工具包。 - Bryan Oakley
抱歉之前表述不够清晰 :) Labjack U12板提供了一个Python框架,并展示了基于Tkinter的键盘和鼠标捕获解决方案,但没有显示器,所以我认为我仍然可以使用Tk机制进行主循环并实现无缝的键盘处理(使用tk)以及其他与应用程序相关的逻辑(这些逻辑可能不是由事件触发的)。答案可能在这里:stackoverflow.com/q/459083 :) - yota

3

这个函数似乎是罪魁祸首。

def move_ball(self):
    while True:
        self.canvas.move(self.ball, 2, 1)
        self.canvas.after(20)
        self.canvas.update()

你在调用它时故意将自己置于无限循环中。
ball1.move_ball()    # gets called, enters infinite loop
ball2.move_ball()    # never gets called, because code is stuck one line above

2

它只移动一个球是因为程序一次只读取一个变量。如果您将程序设置为在球到达某个位置时读取,例如画布的末端,您可以编写程序以读取下一行并触发第二个球的移动。但是,这只会一次移动一个。

您的程序实际上停留在以下行:

ball1.move_ball()

它永远无法到达以下行:

ball2.move_ball()

因为循环没有结束的限制。

否则,“sundar nataraj”的答案可以解决问题。


0
尝试使用 self.canvas.move(self.ball, 2, 1) 的替代方法。
self.canvas.move(ALL, 2, 1)

这段代码用于移动画布中的所有对象。


感谢您的快速回复。如果我在move方法中使用"ALL",它确实会使它们都动起来,但是我希望它们能够独立移动(对于任意数量的球),使用.move(ALL, x, y)将会使画布上所有对象都朝着相同的x和y方向移动。也许我一开始就不应该使用while循环来做这个? - dlohse
@Kazark All 用于移动画布中的所有对象。但 OP 需要尝试不同的方法。我认为他已经明白了重点。 - sundar nataraj
我留下那条评论的原因是因为你的帖子出现在低质量审核队列中。通常,“尝试…”作为评论比作为答案更好。“做…”是一个好答案。 - Keith Pinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接