【问题标题】:Getting a TypeError when trying to increase speed of animation using tkinter尝试使用 tkinter 提高动画速度时出现 TypeError
【发布时间】:2021-07-06 20:29:39
【问题描述】:

我正在尝试完成从入门到使用 Python 编程的家庭作业。 它希望我能够使用向上和向下键分别增加和减少速度。我有汽车的启动(我仍然需要完成形状),我有一个按钮来启动和停止汽车,它工作正常。但是,当我尝试使用书中的示例(increaseSpeed 和 reductionSpeed 函数)运行它时,它给了我“TypeError:increaseSpeed() 需要 1 个位置参数,但给出了 2 个”。我不知道如何在我的代码中纠正这个问题,它开始让我感到沮丧。这本书甚至没有告诉你为什么它使用 sleepTime 来提高它最初用于示例的消息的速度。

from tkinter import *


class controlAnimation:
  def __init__(self):
    window = Tk()
    window.title("Racing Car")


    self.width = 250
    self.height = 100
    self.canvas = Canvas(window, bg="white", width=self.width, height=self.height)
    self.canvas.pack()
    frame = Frame(window)
    frame.pack()
    btStop = Button(frame, text = "Stop", command = self.stop)
    btStop.pack(side=LEFT)
    btStart = Button(frame, text = "Start", command = self.start)
    btStart.pack(side=RIGHT)
    self.x=0

    self.sleepTime = 100
    self.canvas.create_rectangle(self.x,90,self.x+60,80,fill="black", tags = "car")
    self.canvas.create_oval(self.x+10, 90, self.x+20, 100, fill = "black",tags = "car")

    self.dx=3
    self.isStopped = False
    self.animate()



    window.bind("<Up>", self.increaseSpeed)
    window.bind("<Down>", self.decreaseSpeed)
    window.mainloop()
  def stop(self):
    self.isStopped = True
  def start(self):
    self.isStopped = False
    self.animate()
  def increaseSpeed(self):
    if self.sleepTime>5:
    self.sleepTime-=20
  def decreaseSpeed(self):
    self.sleepTime+=20

  def animate(self): # Move the message 
     while not self.isStopped:
      self.canvas.move("car", self.dx,0) 
      self.canvas.after(self.sleepTime) 
      self.canvas.update() 
      if self.x < self.width:
          self.x += self.dx 
      else:
        self.x = 0 

        self.canvas.create_rectangle(self.x-60,90,self.x,80,fill="black", tags = "car")
        self.canvas.create_oval(self.x-50, 90, self.x-40, 100, fill = "black",tags = "car")
controlAnimation()    

【问题讨论】:

  • .bind 函数返回事件,所以你可以做的只是添加另一个参数到 increaseSpeeddecreaseSpeed 函数,例如像这样 increaseSpeed(self, event=None)decreaseSpeed(self, event=None) 或没有None 部分(我认为没有必要设置为无)所以这样可能更紧凑:increaseSpeed(self, event)。您不必在函数中使用该参数
  • 我还建议遵循 PEP 8 并使用 snake_case 作为变量和函数名,使用 CapitalCase 作为类名

标签: python animation tkinter methods typeerror


【解决方案1】:

.bind() 方法返回一个事件并将其传递给给定的函数。

看看这里:

def increaseSpeed(self):
    if self.sleepTime>5:self.sleepTime-=20
def decreaseSpeed(self):
    self.sleepTime+=20

这两个函数都没有任何参数,所以你会看到一个错误

所以你需要做这样的事情:

def increaseSpeed(self,event = None): #or def increaseSpeed(self,*args):
    #[...]
def decreaseSpeed(self,event = None): #or def decreaseSpeed(self,*args):
    #[...]

这是最终的工作代码 -

from tkinter import *


class controlAnimation:
  def __init__(self):
    window = Tk()
    window.title("Racing Car")


    self.width = 250
    self.height = 100
    self.canvas = Canvas(window, bg="white", width=self.width, height=self.height)
    self.canvas.pack()
    frame = Frame(window)
    frame.pack()
    btStop = Button(frame, text = "Stop", command = self.stop)
    btStop.pack(side=LEFT)
    btStart = Button(frame, text = "Start", command = self.start)
    btStart.pack(side=RIGHT)
    self.x=0

    self.sleepTime = 100
    self.canvas.create_rectangle(self.x,90,self.x+60,80,fill="black", tags = "car")
    self.canvas.create_oval(self.x+10, 90, self.x+20, 100, fill = "black",tags = "car")

    self.dx=3
    self.isStopped = False
    self.animate()



    window.bind("<Up>", self.increaseSpeed)
    window.bind("<Down>", self.decreaseSpeed)
    window.mainloop()
  def stop(self):
    self.isStopped = True
  def start(self):
    self.isStopped = False
    self.animate()
  def increaseSpeed(self,event = None): #or def increaseSpeed(self,*args):
    if self.sleepTime>5:self.sleepTime-=20
  def decreaseSpeed(self,event = None): #or def increaseSpeed(self,*args):
    self.sleepTime+=20

  def animate(self): # Move the message 
     while not self.isStopped:
      self.canvas.move("car", self.dx,0) 
      self.canvas.after(self.sleepTime) 
      self.canvas.update() 
      if self.x < self.width:
          self.x += self.dx 
      else:
        self.x = 0 

        self.canvas.create_rectangle(self.x-60,90,self.x,80,fill="black", tags = "car")
        self.canvas.create_oval(self.x-50, 90, self.x-40, 100, fill = "black",tags = "car")
controlAnimation() 

【讨论】:

    【解决方案2】:

    当使用widget.bind("&lt;Event&gt;", func) 时,函数会向函数传递一个额外的事件,您可以根据需要使用它,但显然您不需要它,因此您可以将 init 中的绑定替换为:

        # You can either get the event using lambda if you don't want anything to do with it like this:
        window.bind("<Up>", lambda event: self.increaseSpeed)
        window.bind("<Down>", lambda event2: self.decreaseSpeed)
    

    或者在你的函数中这样做:

    # Or you can pass it as an argument in your function
    def increaseSpeed(self, event):
        if self.sleepTime > 5:
            self.sleepTime -= 20
    
    def decreaseSpeed(self, event):
        self.sleepTime += 20
    

    我相信这就是你想要达到的目标:

    from tkinter import *
    
    
    class CarAnimation:
        def __init__(self):
            self.canvas_width = 550
            self.canvas_height = 280
            self.car_body_width = 100
            self.car_body_height = 50
            self.car_wheel_width = 20
            self.car_wheel_height = 20
            self.car_top_left = 0
            self.move_by = 1
            self.delay = 20
            self.default_delay = 10
            self.car_is_moving = False
            self.root = Tk()
            self.root.resizable(False, False)
            self.root.title("Car Animation!")
            self.car_canvas = Canvas(self.root, width=self.canvas_width, height=self.canvas_height, background="white",
                                     highlightthickness=0)
            # If you have more shapes in the canvas that you do not want to get affected by the movement store them as
            # below and call the move function for each of them, I just stored them in a variable for demonstration purposes
            self.car_body = self.car_canvas.create_rectangle(0, 0, self.car_body_width, self.car_body_height)
            self.car_wheel1 = self.car_canvas.create_oval(0, self.car_body_height, self.car_wheel_width,
                                                          self.car_body_height+self.car_wheel_height)
            self.car_wheel2 = self.car_canvas.create_oval(self.car_body_width-self.car_wheel_width, self.car_body_height,
                                                          self.car_body_width, self.car_body_height+self.car_wheel_height)
            self.start_btn = Button(self.root, text="Start Car", command=self.start_car)
            self.stop_btn = Button(self.root, text="Stop Car", command=self.stop_car)
            self.car_canvas.pack(side=TOP)
            self.start_btn.pack(side=BOTTOM, pady=5)
            self.stop_btn.pack(side=BOTTOM)
            self.root.bind("<Up>", self.decrease_delay)
            self.root.bind("<Down>", self.increase_delay)
            self.root.mainloop()
    
        def start_car(self):
            if not self.car_is_moving:
                self.car_is_moving = True
                self.move_car()
    
        def stop_car(self):
            if self.car_is_moving:
                self.car_is_moving = False
    
        def move_car(self):
            # Instead of storing the car's top left position and appending to it, you can also
            # 'self.car_canvas.coords(self.car_body)[0]' so that it would return the shape's coords
            if self.car_is_moving and self.car_top_left < self.canvas_width:
                self.car_top_left += self.move_by
                # You can store their layer numbers and move them one by one but since there were no more shapes
                # I decided to move all of them at once using 'ALL'
                self.car_canvas.move(ALL, self.move_by, 0)
                self.root.after(self.delay, self.move_car)
            elif self.car_is_moving and self.car_top_left >= self.canvas_width:
                self.car_top_left += -self.canvas_width - self.car_body_width
                self.car_canvas.move(ALL, -self.canvas_width - self.car_body_width, 0)
                self.root.after(self.delay, self.move_car)
    
        def increase_delay(self, event):
            # Added the if statement below so that the user cannot change the delay when the car is not moving
            if self.car_is_moving:
                self.delay += 10
    
        def decrease_delay(self, event):
            if self.delay > self.default_delay and self.car_is_moving:
                self.delay -= 10
            # You can just delete this elif statement, I just wanted to print out something to the terminal
            elif self.delay <= self.default_delay and self.car_is_moving:
                print("Delay cannot be lower than default!")
    
    
    if __name__ == '__main__':
        CarAnimation()
    

    请注意,如代码中所述,您可以使用“self.car_canvas.coords()”而不是存储左上角。如果您对代码有任何疑问,请在 cmets 中提问

    【讨论】:

      猜你喜欢
      • 2017-12-25
      • 2023-03-24
      • 2022-10-21
      • 2014-02-16
      • 1970-01-01
      • 1970-01-01
      • 2017-06-01
      • 2020-05-10
      • 2011-09-20
      相关资源
      最近更新 更多