Python Forum

Full Version: animated rain in tkinter
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi!
I am trying to simulate rain, similar to this example: hxxps://www.youtube.com/watch?v=e5LCLDoVx2I
The code you see below can so far create drops in random colors in random locations at the top of the canvas. However, I can't make the rain fall down, no matter what I try. Can someone give me a hint on how to make each drop fall down individually? As you can see, I save the raindrop-objects in a list, and basically when one drop reaches the ground I want to delete it and create a new one.
Btw, the code I already have can probably be improved a lot since I am very new to programming, feel free to critique!
Thank you!!

from tkinter import *
from random import randint
import random

root=Tk()

width = 400
height = width
colors = ["red", "orange", "yellow", "green", "blue", "violet", "white"]
dropcount = width//2    
drops = []

background = Canvas(root, width=width, height=height, background ='black')
background.pack()

class Drop():
    def __init__ (self, locationx, locationy, heightdrop, widthdrop, color):
        self.locationx = locationx
        self.locationy = locationy
        self.heightdrop = heightdrop
        self.widthdrop = widthdrop
        self.color = color

    def newdrop(self):
        drop=background.create_rectangle(self.locationx, self.locationy, self.locationx+self.widthdrop, self.locationy+self.heightdrop, fill=self.color)
        drops.append(drop)

for i in range (dropcount):
    drop=Drop(randint(0, width), randint(-100, 100), 10, 2, random.choice(colors))
    drop.newdrop()

root.mainloop()
You are not using class incorrectly.
The class should be instantiated once only, so outside of the loop
This leads to another problem in that all of the rectangle variables are passed to init
which will only be run once when instantiated.

You need to pass the variable information to the newdrop method instead of init.
In init, you should declare the variables, with default values, and override from newdrop.

Which means the randomness should be moved to the call to newdrop, or better yet within the method itself.
Once you call root.mainloop(), you pass control of the program over to tkinter to handle input events and whatnot.  So, before then, you need to setup a callback to happen after a set period of time.  Off the top of my head, the easiest way to do that is with root.after().  I added this to your code, just before you call mainloop():
def move_drops():
    for drop in drops:
        background.move(drop, 0, 5)
    root.after(100, move_drops)


root.after(100, move_drops)
root.mainloop()
Now they all move downward, but that doesn't handle killing them when they hit the bottom, or spawning new ones.  But this should be good enough for you to keep going :)
if you combine nilamo's code with the following, you will probably have what you want
One thing, before you paint the second set, you should blank out the previous image
this code prints more rain, but doesn't move.

from tkinter import *
from random import randint
import random

root = Tk()

width = 400
height = width
colors = ["red", "orange", "yellow", "green", "blue", "violet", "white"]
dropcount = width // 2
print(f'dropcount: {dropcount}')
drops =

background = Canvas(root, width=width, height=height, background='black')
background.pack()


class Drop():
    def __init__(self):
        self.locationx = None
        self.locationy = None
        self.heightdrop = None
        self.widthdrop = None
        self.color = None

    def newdrop(self, locationx, locationy, heightdrop, widthdrop, color):
        self.locationx = locationx
        self.locationy = locationy
        self.heightdrop = heightdrop
        self.widthdrop = widthdrop
        self.color = color
        drop = background.create_rectangle(self.locationx, self.locationy, self.locationx + self.widthdrop,
                                           self.locationy + self.heightdrop, fill=self.color)
        drops.append(drop)


drop = Drop()
for i in range(dropcount):
    drop.newdrop(randint(0, width), randint(-100, 100), 10, 2, random.choice(colors))

root.mainloop()
Thank you very much! I implemented your suggestions and it's working so far! I'll try to figure out the rest myself now