0

I've been using this as a template:

Blobs Animation

and pretty much the only difference I can find is he uses the line:

from Tkinter import *

whereas I use the line

import Tkinter as Tk

I thought I accounted for this by making all my calls to Tkinter modules as Tk.(Blob.py equivalent). I can this blob animation example to run, but my program gets the error:

'Nonetype' object not callable

on the line:

allStars[i] = allStars[i]()

from my code below (this particular line is in the last block of code):

class Star:

    def __init__(self, canvas, xy, delta):

        self.canvas = canvas
        self.delta = delta

        self.id = self.canvas.create_rectangle(
            xy[0],xy[1],
            xy[0]+starSize,xy[1]+starSize,
            fill='white'
            )


    def __call__(self):
        return self.down

    def down(self):

        xy = self.canvas.coords(self.id)
        if xy[1] >= gameScreenHeight:
            x2 = random.randint(1,gameScreenWidth)
            y2 = 0;
            self.canvas.coords(self.id,x2,y2)
        else:
            self.canvas.move(self.id, 0, self.delta)


root = Tk.Tk()
root.title("Stars")
root.resizable(0,0)
TkPlacement = "%dx%d%+d%+d" % (gameScreenWidth, gameScreenHeight, 10*starSize, heightOffset)
root.geometry(TkPlacement)

frame = Tk.Frame(root, bd = 5, relief=Tk.SUNKEN)
frame.pack()

space = Tk.Canvas(frame, width = gameScreenWidth, height = gameScreenHeight, cursor = 'plus')
space.pack()
space.create_rectangle(0,0,gameScreenWidth,gameScreenHeight,fill="black")



allStars = [
    Star(space, (10,30),3),
    Star(space, (15,60),3),
    Star(space, (80,50),5)
    ]

#N = 99;

#for i in range(N):
#   x1 = random.randint(1,gameScreenWidth)
#   y1 = random.randint(1,gameScreenHeight)
#   aStar = Star(space, (x1,y1), 3)
#   allStars.append(aStar)

root.update()

try:
    while 1:
        for i in range(len(allStars)):
            allStars[i] = allStars[i]()
            root.update_idletasks() # redraw
        root.update() # process events
except Tk.TclError:
    pass # to avoid errors when the window is closed
Keegan Keplinger
  • 627
  • 5
  • 15

2 Answers2

2

Early in your code you set allStars[i] to a function (self.done). This function returns None by virtue of not having an explicit return statement.

When you do this:

allStars[i] = allStars[i]()

... You are replacing the value of allStars[i] with the result of calling allStars[i], which is None. The next time this line of code executes, allStars[i] is None, so you get that error.

If you are trying to do animation, there are better ways than to have your own infinite loop running, since you already have an infinite loop running -- mainloop. For example, you can remove the entire while loop and try/catch block at the end of your code with the following:

def redraw():
    for i in range(len(items)):
        items[i] = items[i]()
    root.after(int(1000/30), redraw) # 30 frames per second

root.after_idle(redraw)
root.mainloop()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thank you for your answer. In the blobs example, the author omits the mainloop function and uses the while loop. I assumed it was for future considerations of synchronization, but this is just based loosely on what I've read about sync difficulties with Tkinter. I am new to python so I'm mostly just following the author's example and not entirely understanding everything I'm doing yet. – Keegan Keplinger May 20 '13 at 12:52
  • "sync difficulties"? What does that mean? I didn't realize there was no invocation of `mainloop`. That is a very strange way to use Tkinter, and isn't recommended. The example you are using is a poor example of how to do animation with tkinter. – Bryan Oakley May 20 '13 at 12:56
  • I can't find the particular reference, but people were basically stating a preference of pygame over tkinter because it handles synchronization automatically, whereas tkinter requires more careful attention. I chose Tkinter because I personally prefer to use standard libraries where I can and the disadvantage (so I've read) for pygame is that it can be slow and clunky. I'm not sure what I'd replace mainloop with. – Keegan Keplinger May 20 '13 at 13:00
  • by the way, do you know of a better animation example that uses better standards? – Keegan Keplinger May 20 '13 at 16:45
  • @Xurtio: see http://stackoverflow.com/a/11505034/7432 – Bryan Oakley May 20 '13 at 16:55
  • Thank again for your input. Just to be sure, if these animations will eventually become interactive (such as a game) is this still the correct approach; it appears you just put your listening events right before the frame redraw, correct? – Keegan Keplinger May 20 '13 at 18:02
  • @Xurtio: yes, this is the correct approach. Leveraging the event loop in this way allows the UI to remain responsive. – Bryan Oakley May 20 '13 at 19:43
1

down() returns None (implicitly); and the first time through your infinite loop you're replacing everything in allStars with it.

allStars[i] = allStars[i]() is probably not what you want, although it's not clear exactly what you're trying to accomplish with that.

Wooble
  • 87,717
  • 12
  • 108
  • 131