2

My aim is to instantiate a class called arrow, so I can have more arrows then just 1. I wanted to start with the coordinates 200, 200 and want to increase x by 15 per every 100 milliseconds. But when I try to execute this code it gives me following error:

  File "game.py", line 25, in moveArrow
    self.after(100, self.moveArrow(arrow, xCoord+15, yCoord)) #repeat, changing x
  File "game.py", line 24, in moveArrow
    arrow.place(x = xCoord, y = yCoord) #replace with new x,y
  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1860, in place_configure
    + self._options(cnf, kw))
  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1055, in _options
    elif isinstance(v, (tuple, list)):
RuntimeError: maximum recursion depth exceeded while calling a Python object

The "File "game.py", line 25, in move Arrow self.after(100, self.moveArrow(arrow, xCoord+15, yCoord)) #repeat, changing x" gets repeated very often as well.

from Tkinter import *
from random import randint
from PIL import ImageTk, Image

class App(Frame):
        def __init__(self, master=None):
                Frame.__init__(self, master, height=400, width=400)
                self.master = master
                self.master.bind('<Shift_L>', self.createArrow)
        def createArrow(self, event):
                self.arrow = Arrow(self)
                self.arrow.moveArrow(self.arrow, 200, 200)

class Arrow(Frame):
        def __init__(self, master):
                Frame.__init__(self, master)
                self.arrowImage = ImageTk.PhotoImage(Image.open("arrow.gif"))
                Label(self, image=self.arrowImage).pack()
        def moveArrow(self, arrow, xCoord, yCoord):
                arrow.place_forget()
                arrow.place(x = xCoord, y = yCoord)
                self.after(100, self.moveArrow(arrow, xCoord+15, yCoord))

root = Tk()
root.title("Mein erstes Spiel")
app = App(master=root).pack()
root.mainloop()
BoJack Horseman
  • 4,406
  • 13
  • 38
  • 70

3 Answers3

10

The other answers are correct about the source of the problem being this line:

self.after(100, self.moveArrow(arrow, xCoord+15, yCoord))

But the answer is Tkinter specific:

Take a look at the docs for the after method to see how to properly implement this method. Calling it like a normal function call will do just that and throw your program into an infinite loop when the control flow reaches that function call. When you use after, you have two options:

Passing the time arg, then the callback, then the callback args:

self.after(100, self.moveArrow, arrow, xCoord+15, yCoord)

Or, using a lambda expression to hold the function call:

self.after(100, lambda: self.moveArrow(arrow, xCoord+15, yCoord))
atlasologist
  • 3,824
  • 1
  • 21
  • 35
  • http://effbot.org/tkinterbook/photoimage.htm <-- take a look at that if you're having a problem with the image, particularly the note at the bottom about how to keep a reference. I only tested with a text Label. – atlasologist Apr 09 '14 at 11:05
  • 2
    There's a lot of good info here: http://effbot.org/tkinterbook/tkinter-index.htm ... other than that, just make stuff and you'll figure it out – atlasologist Apr 09 '14 at 11:09
1

You are calling self.moveArrow(arrow, xCoord+15, yCoord) inside moveArrow() method.

so you have an endless recursion with no break at any stage. if you want to understand how to build python recursion method you can read here

If you want to create some simple moving effect, then just do it on a loop lets say that you get moving the arrow at x + 200 and y + 200 generate a simple for loop and move the arrow with a delay.

pseudo code example:

def moveArrow(....)
    loop:
        x += 10
        y += 10
        change_arrow_place(...)
Community
  • 1
  • 1
Kobi K
  • 7,743
  • 6
  • 42
  • 86
  • This is absolutely the wrong way to do animation in tkinter. You should never, ever call `sleep` in a GUI. – Bryan Oakley Apr 09 '14 at 10:41
  • @Bryan Oakley You are right since sleep will freeze the entire gui, it was just for example. – Kobi K Apr 09 '14 at 10:53
  • @KobiK: if you know it is the wrong way, you shouldn't be giving it as an example. At the time I write this comment, this is the accepted answer. Therefore, the person asking the question thinks this is the right way to do it, and will have learned an incorrect way to write code. – Bryan Oakley Apr 09 '14 at 10:56
  • @Bryan Oakley the problem was recursion without break, it has noting to do with the sleep, i will delete the sleep part since your comment is true. – Kobi K Apr 09 '14 at 10:59
  • @KobiK: the problem was recursion, but not because of the lack of a break. The problem is that there was recursion in the first place. When `after` is called properly, there is no recursion. This answer has the right explanation: http://stackoverflow.com/a/22959959/7432 – Bryan Oakley Apr 09 '14 at 11:15
0

Line which create problem is

self.after(100, self.moveArrow(arrow, xCoord+15, yCoord))x

Because moveArrow has no break condition. When ever we use recursion in programming language, recursion need break condition from where it will not call further same function.

for example : prime number in recursion

int isPrime(int num,int i){

    if(i==1){
        return 1;
    }else{
       if(num%i==0)
         return 0;
       else
         isPrime(num,i-1);
    }
}

in above code break conditions are if (i==1) and if(num%i==0) in these 2 condition it will not call isPrime function and recursion will terminate there.

Please add break condition and run again.

Nilesh
  • 20,521
  • 16
  • 92
  • 148
  • You are correct about the line, you are incorrect about the explanation. – Bryan Oakley Apr 09 '14 at 10:43
  • If you can explain me what is incorrect in explanation, then it will be clear my view also :) – Nilesh Apr 09 '14 at 10:50
  • 1
    The correct explanation is in this answer: http://stackoverflow.com/a/22959959/7432. The problem has nothing to do with needing a "break" condition. The problem has to do with how `after` is called. – Bryan Oakley Apr 09 '14 at 10:54