-1

I am trying to make an animation that uses heart rate date as the trigger using raspberry pi. I have used github code https://github.com/tutRPi/Raspberry-Pi-Heartbeat-Pulse-Sensor and am using the example.py as a basis for my work.

I have also used a great tutorial that makes balls move around the screen using tkinter which works.

I am totally new to python and am having trouble getting the animation to play on the if statement. Ideally I want the balls to move at speed generated by the bmp data, so that faster heart rates will make them move around faster. I seem to be very far from this at the moment. If anyone can help to get this working I will be forever grateful. as it stands, the balls appear but do not move. I think there is a conflict with the movement and the bpm updating...

Here is the code:

from pulsesensor import Pulsesensor
import time
from tkinter import *
import random

tk = Tk()
WIDTH=1500
HEIGHT=800
canvas = Canvas(tk, bg="brown4", height=HEIGHT, width= WIDTH)
tk.title("drawing")
canvas.pack()

##below is the class to create multiple balls that are coloured
##and move and detect the edge and bounce

class Ball:
    def __init__(self, color, size):
        self.shape  = canvas.create_oval (10, 10, size,  size, fill=color, 
                                          outline=color, stipple="gray25")
        self.xspeed = random.randrange(-1,5)
        self.yspeed = random.randrange(-1,5)

    def move(self):
        canvas.move(self.shape, self.xspeed, self.yspeed)
        pos = canvas.coords(self.shape)
        if pos[3]>=HEIGHT or pos[1]<=0:
            self.yspeed=-self.yspeed
        if pos[2] >=WIDTH or pos[0] <=0:
            self.xspeed=-self.xspeed


colors=["red4", "red3", "OrangeRed2","OrangeRed4","firebrick3"]
##this is make 100 balls
balls=[]
##this is to set the colour and size of the balls which is randomised:
for i in range (100):
    balls.append(Ball(random.choice(colors), random.randrange(150, 200)))

##this is to call the balls
##while True:

p = Pulsesensor()
p.startAsyncBPM()

try:
    while True:
        bpm = p.BPM
        if bpm > 0:
            print("BPM: %d" % bpm)
            for ball in balls:
                    ball.move()
            tk.update()
            time.sleep(0.02)
            tk.mainloop()
        else:
            print("No Heartbeat found")
        time.sleep(1)
except:
    p.stopAsyncBPM()
SneakyTurtle
  • 1,831
  • 1
  • 13
  • 23
  • Where are you changing the speed of the balls? It doesn't seem to be anywhere. – Rob Oct 09 '17 at 15:11
  • Hi Rob, thanks for the reply. I am not changing the speed as yet. That is the end goal, but I think I am doing something wrong in calling the class? – Tina Burton Oct 09 '17 at 15:19
  • 1
    You picked a bad tutorial for how to do animation. See https://stackoverflow.com/a/25431690/7432 – Bryan Oakley Oct 09 '17 at 15:53
  • thank you Bryan, I am trying to figure it out now – Tina Burton Oct 09 '17 at 16:00
  • Sadly I failed to understand what is different from what I have apart from the way in which you are calling the functions. This did not work as follows try: while True: bpm = p.BPM if bpm > 0: ball.move() root.mainloop() else: print("No Heartbeat found") time.sleep(1) – Tina Burton Oct 09 '17 at 16:39
  • For one, you must call `mainloop` exactly once. You're calling it in a loop, which almost certainly isn't doing what you think it's doing. For another, you're calling `sleep` which you should never do in the main thread of a GUI. – Bryan Oakley Oct 09 '17 at 21:45

1 Answers1

1

I don't have a Raspberry-Pi or a Pulse Sensor, so the following could only be tested to a certain degree—but hopefully it will provide you better foundation code. I reduced the number of balls to make it easier to see what's going on. I don't really understand how their movement is supposed to relate to pulse-rate, so you'll need to flesh that out yourself...

When using tkinter it's important to understand that everything that happens must do so via the mainloop() which generally doesn't return until the application script exits (so it's essentially an endless loop). This means you generally can't just call it whenever you want. In this case, a "polling" function is used and it is called at set intervals to update the Canvas object—which will all happens while the mainloop() is running.

from pulsesensor import Pulsesensor
import random
from tkinter import *

WIDTH=1500
HEIGHT=800
COLORS = ["red4", "red3", "OrangeRed2", "OrangeRed4", "firebrick3"]
DELAY = 200  # In millisecs
NUMBALLS = 5

class Ball:
    def __init__(self, color, size):
        self.shape = canvas.create_oval(10, 10, size,  size, fill=color,
                                        outline=color, stipple="gray25")
        self.xspeed = random.randrange(-1, 5)
        self.yspeed = random.randrange(-1, 5)

    def move(self):
        canvas.move(self.shape, self.xspeed, self.yspeed)
        pos = canvas.coords(self.shape)
        if pos[3] >= HEIGHT or pos[1] <= 0:
            self.yspeed = -self.yspeed
        if pos[2] >= WIDTH or pos[0] <= 0:
            self.xspeed = -self.xspeed

def poll(p):
    try:
#        bpm = p.BPM
        bpm = random.randint(0, 200)  # Random value for testing.
        if bpm < 1:
#            print("No Heartbeat found")
            pass
        else:
#            print("BPM: %d" % bpm)
            for ball in balls:
                ball.move()
    except Exception as exc:
        print('Exception raised: {}'.format(exc))
        p.stopAsyncBPM()
        root.quit()

    root.after(DELAY, poll, p)  # Call this function again after delay.

if __name__ == '__main__':
    root = Tk()
    root.title("Beating Heart")
    canvas = Canvas(root, bg="brown4", height=HEIGHT, width=WIDTH)
    canvas.pack()
    balls = [Ball(random.choice(COLORS), random.randrange(150, 200))
                for _ in range(NUMBALLS)]

    p = Pulsesensor()
    p.startAsyncBPM()
    poll(p)  # Start polling.
    root.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301
  • 1
    You shouldn't need the call to `update_idletasks` inside of `poll`. The update happens automatically between `poll` returning and running again. – Bryan Oakley Oct 09 '17 at 21:46
  • @Bryan: Removed. Wasn't sure if it was really needed, so put it in just in case. Once again, thanks for the tip. – martineau Oct 09 '17 at 21:48
  • 1
    Thank yo u both for your help. I will let you know once I have finished it and hopefully share the results! All the best – Tina Burton Oct 10 '17 at 13:39
  • Thank you for all your help. I have another question, but cannot seem to paste the code here so I will try and start a new thread. thanks again. – Tina Burton Oct 16 '17 at 12:26