【问题标题】:Python Tkinter AnimationPython Tkinter 动画
【发布时间】:2012-10-24 07:00:04
【问题描述】:

为什么动画不工作?运行程序时,形状不会移动。

from Tkinter import *
import time



class alien(object):
     def __init__(self):
        self.root = Tk()
        self.canvas = Canvas(self.root, width=400, height = 400)
        self.canvas.pack()
        alien1 = self.canvas.create_oval(20, 260, 120, 360, outline='white',         fill='blue')
        alien2 = self.canvas.create_oval(2, 2, 40, 40, outline='white', fill='red')
        self.canvas.pack()
        self.root.mainloop()

     def animation(self):
        track = 0
        while True:
        x = 5
        y = 0
        if track == 0:
           for i in range(0,51):
                self.time.sleep(0.025)
                self.canvas.move(alien1, x, y)
                self.canvas.move(alien2, x, y)
                self.canvas.update()
           track = 1
           print "check"

        else:
           for i in range(0,51):
                self.time.sleep(0.025)
                self.canvas.move(alien1, -x, y)
                self.canvas.move(alien2, -x, y)
                self.canvas.update()
           track = 0
        print track

alien()

【问题讨论】:

  • 请多加问号
  • 试图让椭圆在 x 轴上从左向右移动
  • 有什么需要导入的吗?因为我在网上查了论坛,只导入 Tkinter 和计时器是有意义的,不知道我错过了什么

标签: python animation python-2.7 tkinter


【解决方案1】:

您的animation 方法中有一个永不中断的while True 循环。这在 GUI 程序中是一个禁忌,因为从不返回,它会阻止 GUI 的事件循环处理事件。因此,例如,如果您有一个菜单,那么用户将无法选择任何菜单项。除了您在 animation 方法中执行的任何操作之外,GUI 将显示为冻结状态。

这里是对@Tim 代码的轻微修改,它通过删除while 循环并在返回前简单地移动外星人一步来解决这个问题。 self.master.afteranimation 方法的末尾被调用,让事件循环在短暂的暂停后再次调用动画。


import tkinter as tk
import time

class Alien(object):
    def __init__(self, canvas, *args, **kwargs):
        self.canvas = canvas
        self.id = canvas.create_oval(*args, **kwargs)
        self.vx = 5
        self.vy = 0

    def move(self):
        x1, y1, x2, y2 = self.canvas.bbox(self.id)
        if x2 > 400:
            self.vx = -5
        if x1 < 0:
            self.vx = 5
        self.canvas.move(self.id, self.vx, self.vy)

class App(object):
    def __init__(self, master, **kwargs):
        self.master = master
        self.canvas = tk.Canvas(self.master, width=400, height=400)
        self.canvas.pack()
        self.aliens = [
            Alien(self.canvas, 20, 260, 120, 360,
                  outline='white', fill='blue'),
            Alien(self.canvas, 2, 2, 40, 40, outline='white', fill='red'),
        ]
        self.canvas.pack()
        self.master.after(0, self.animation)

    def animation(self):
        for alien in self.aliens:
            alien.move()
        self.master.after(12, self.animation)

root = tk.Tk()
app = App(root)
root.mainloop()

【讨论】:

  • o ic,但是 from Tkinter import * 和 import Tkinter as tk 有什么区别??
  • from Tkinter import * 导入Tkinter 模块并将Tkinter 模块中的所有(公共)全局变量添加到当前模块的命名空间中。 import Tkinter as tk 导入 Tkinter 模块并将模块对象本身(tk 引用)添加到当前模块的命名空间。我从不使用from modulename import *,因为它使追踪变量的来源变得更加困难。有关在模块中使用导入的“最佳实践”的建议,请参阅 this FAQ
  • @unutbu 很好,如果我们想使用更复杂的动画 gui,我们将动画放在“animation”方法中,然后 gui 本身,与事件管理之类的东西应该放在或在 self.master.after 之后由 _____init_____ 函数调用。我是对的 ?还是应该在之前?
  • @Yann:我会像往常一样构建 GUI(通常在 App.__init__ 中完成)并在 App.__init_ 结束时调用 self.master.after(0, self.animation)。可以这样想:你得到所有的小部件,然后在事件循环中添加一个动作(self.animation 的调用)。
  • @unutbu 好的!我在上面。感受python的力量:3
【解决方案2】:

您从未调用过animation 方法。还有一些其他的命名问题。

# Assuming Python 2.x
# For Python 3.x support change print -> print(..) and Tkinter to tkinter
from Tkinter import *
import time

class alien(object):
     def __init__(self):
        self.root = Tk()
        self.canvas = Canvas(self.root, width=400, height = 400)
        self.canvas.pack()
        self.alien1 = self.canvas.create_oval(20, 260, 120, 360, outline='white',         fill='blue')
        self.alien2 = self.canvas.create_oval(2, 2, 40, 40, outline='white', fill='red')
        self.canvas.pack()
        self.root.after(0, self.animation)
        self.root.mainloop()

     def animation(self):
        track = 0
        while True:
            x = 5
            y = 0
            if track == 0:
               for i in range(0,51):
                    time.sleep(0.025)
                    self.canvas.move(self.alien1, x, y)
                    self.canvas.move(self.alien2, x, y)
                    self.canvas.update()
               track = 1
               print "check"

            else:
               for i in range(0,51):
                    time.sleep(0.025)
                    self.canvas.move(self.alien1, -x, y)
                    self.canvas.move(self.alien2, -x, y)
                    self.canvas.update()
               track = 0
            print track

alien()

【讨论】:

  • 哇,我不会发现这些错误,你能提供一些关于如何成为优秀程序员的提示吗?任何可以帮助我提高这个爱好的网站或书籍,因为我觉得它很有趣....非常感谢先生!!!
  • 尽量多练习,注意小细节。在这种情况下,通过在程序运行时读取错误消息来识别所有命名问题。
  • 在代码中加入 sleep 是个坏主意。尽管它们很短,但每次您睡觉时,整个 GUI 都会无响应。
【解决方案3】:

这是一种使用循环的方法:

from tkinter import * # version 3.x

tk = Tk()

frame = Frame(tk)
canvas = Canvas(frame) # use canvas

frame.pack(fill = BOTH, expand = 1)
canvas.pack(fill = BOTH, expand = 1)

ball = canvas.create_oval(10, 10, 30, 30, tags = 'ball') # create object to animate

def animation(x_move, y_move):
    canvas.move(ball, x_move, y_move) # movement
    canvas.update()
    canvas.after(20) # milliseconds in wait time, this is 50 fps

    tk.after_idle(animation, x_move, y_move) # loop variables and animation, these are updatable variables

animation(2, 2) # run animation

可更新变量是在更新时保持不变并且可以再次更新的变量。

【讨论】:

  • 通过删除canvas.update()canvas.after_idle(...) 并改为使用canvas.after(20, animation, x_move, y_move),我得到了更流畅的程序。在最后一行之后添加tk.mainloop()
  • 我被告知,但我现在在 c++ 中使用带有着色器和 3d 照明的高级 Opengl,感谢您的输入! Opengl 虽然比 tkinter 好...
猜你喜欢
  • 2020-05-15
  • 1970-01-01
  • 1970-01-01
  • 2022-11-02
  • 1970-01-01
  • 2021-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多