0

I have the following code that almost does what I need:

import threading
import sched, time


def printit():
  threading.Timer(10.0, printit).start()
  print("Hello, World!")
  
  
x=25
printit()

while x>1:
    time.sleep(1)
    print(x)
    x=x-1 

What it does is that it prints "Hello, World!" every 5 seconds while the number counts down from 25 to 2 simultaneously. The issue I'm having is that I want to put this "Hello, World!" within the loop so that it will stop when the countdown stops. When I add it to the loop, it says "Hello, World!" every second and also every 5 seconds, and when the loop ends, it still continues. I think I'm not using the correct module but I'm not sure.

Edit: This is the code I'm trying to apply it to:

import threading
import sched, time
import numpy as np
import cv2
import imutils


flag = False
def printit():
    while(flag):
        print("Hello, world!")
        time.sleep(30)
          
t = threading.Thread(target=printit)
t.start()

fullbody_cascade = cv2.CascadeClassifier('haarcascade_fullbody.xml') #creates variables for the different cascades
upperbody_cascade = cv2.CascadeClassifier('haarcascade_upperbody.xml')
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
cap0 = cv2.VideoCapture(0) #chooses camera to be utilized

while True:
    
    ret0, frame0 = cap0.read() #reads the frames of the chosen camera
    
    gray0 = cv2.cvtColor(frame0, cv2.COLOR_BGR2GRAY) #converts to grayscale
     
    fullbody0 = fullbody_cascade.detectMultiScale(gray0) #detects the grayscaled frame
    upperbody0 = upperbody_cascade.detectMultiScale(gray0)
    face0 = face_cascade.detectMultiScale(gray0)
     
    for (x,y,w,h) in fullbody0:
        cv2.rectangle(frame0, (x,y), (x+w, y+h), (0,0,255), 2) #creates red a box around entire body
        
    for (x,y,w,h) in upperbody0:
        cv2.rectangle(frame0, (x,y), (x+w, y+h), (255,0,0), 2) #creates a blue box around upper body
    
    for (x,y,w,h) in face0:
        cv2.rectangle(frame0, (x,y), (x+w, y+h), (0,255,0), 2) #creates a green box around face
        flag = True
        time.sleep(0.5)
        
            
    flag = False 
    
    cv2.imshow('cam0',frame0)
    k = cv2.waitKey(30) & 0xff
    if k == 27:
        break

cv2.destroyAllWindows()
  

I tried implementing it like this but it doesn't work as intended. If a condition of one of the for loops is met rapidly, it will rapidly print "Hello, World!", how can I change this such that it will always only print "Hello, World!" every x seconds no matter how many times the for loop conditions are met.

Edit 2: The code above works now as I did some slight editing to it, the issue was that the for loop was moving too quickly such that the print message would not display, by adding time.sleep(0.5) after the flag = True statement, it was able to print the message. Thank you Orius for the help!

2 Answers2

0

Try to do

t = threading.Timer(5.0, printit)
t.start()

instead of

threading.Timer(5.0, printit).start()

and then, when you want it to stop (after the while loop has finished), do

t.cancel()

Hope this helps!

Edit:

Oh, you need t to be accessible from outside of printit(). You can declare it outside of printit(), or have printit() return it, for example.

Edit 2:

Sorry, there's also the problem that you pass printit() to the timer, where printit() itself is the one who creates the timer, so there's a loop.

Here's how it should be done:

import threading
import sched, time


def printit():
  print("Hello, World!")

t =  threading.Timer(5.0, printit)
t.start() 
  
x=25
printit()

while x>1:
    time.sleep(1)
    print(x)
    x=x-1

t.cancel()

Edit 3:

With Thread instead of Timer:

import threading
import sched, time

flag = True

def printit():
    while(flag):
        time.sleep(5)
        print("Hello, world!")  
  
t = threading.Thread(target=printit)
t.start()

x=25

while x>1:
    time.sleep(1)
    print(x)
    x=x-1 

flag = False
Orius
  • 1,093
  • 5
  • 11
  • Thank you for the quick response, I tested this code but it only prints "Hello, World!" once, but I want it to keep printing until the 25 seconds are done. – Jabari Lindsay Aug 04 '20 at 02:50
  • I actually tried this but for some reason, when I use that method of using "t", it only prints once, I'm not sure why because it is basically the same code just written differently. – Jabari Lindsay Aug 04 '20 at 03:09
  • Oh, right, right, sorry. I just wouldn't use threading.Timer for that (I'd start a thread that runs an infinite loop that prints "Hello, world!" every 5 seconds, using time.sleep(), and then kill that thread once the countdown is finished). But let me see if I can think of a way to do what you want the way you want to do it, with recursive use of threading.Timer. – Orius Aug 04 '20 at 03:16
  • No, it's not the same code at all, I was just confused and forgot you want "Hello, world!" to keep printing as long as the countdown is still going on. In my version printit() doesn't start a new timer, so there's only one timer, and when it's done it's done. – Orius Aug 04 '20 at 03:19
  • Thank you, but it doesn't have to use threading.Timer, my main objective is to have a piece of code to be running like regular in the while loop while the other piece of the code within the while loop is running every x seconds. I think I may need to use events and interrupts to achieve this? I know there are many methods so I'm fine with any once I can understand it. I can explain further what my actual code does if you like so you can get a better understanding of what I'm trying to do? – Jabari Lindsay Aug 04 '20 at 03:21
  • If you don't insist on Timer, then see my third edit above. – Orius Aug 04 '20 at 03:30
  • The way my actual code works, I want to print a message when it goes into the loop, but the loop is different because its not a simple countdown and ends but its a for x in y loop meaning depending on the value seen, it can go back into that loop so I would prefer the timer not being within the loop because I ONLY want it to print when it's within the loop once every 5 seconds, but I am thinking if the timer is within the loop, it will print multiple times if the value seen fits the criteria, I hope you understand what I'm trying to say. – Jabari Lindsay Aug 04 '20 at 03:32
  • Thank you, your third edit does what I want it to do! I am not sure if this method will work within my actual code but I will let you know! – Jabari Lindsay Aug 04 '20 at 03:34
  • I'm not sure what you mean by "it can go back into that loop", but if the main thread can go back and forth between "now I want those messages to be printed every 5 secs" and "now I don't", you can change "while (flag):" to "while (true):", and wrap "print("Hello, world!")" in an if statement, with flag as the condition. This way, the message-printing thread will go on forever, but it will only print when the main thread signals it should via that flag. Do you understand what I mean? – Orius Aug 04 '20 at 03:40
  • I sort of understand what you mean and this seems like the exact solution I need, let me explain my code in more detail, it is a large while true loop, with 3 for loops within it, the while true loop is infinite until stopped and each for loop is went through over and over. Would you like to see the code? – Jabari Lindsay Aug 04 '20 at 03:45
  • And you want those messages to be printed only when the for loops are in action, right? Then you should use my latest suggestion: the message-printing thread should do "while (true)" and have the print command wrapped in "if (flag)", and in the main thread, the "while (true)" loop should set "flag = True" before entering the for loops and set "flag = False" after exiting from them. Try that, and let me know if it doesn't do what you want. – Orius Aug 04 '20 at 03:57
  • I updated my post to resemble what you told me to do, the issue I'm getting now is that once the conditions are met and the last for loop does what it has in it, even if i remove the conditions, it still continues to say "Hello, World!". Any idea what's wrong with my code? – Jabari Lindsay Aug 04 '20 at 04:09
  • Okay so I got it to stop when the conditions are not met BUT now "Hello, World!" is occurring rapidly because the for loop's condition is being met rapidly, so I'm not sure if this method will work, see code in my original post about that. What must I do to fix this issue? I want it to say "Hello, World!" every x seconds no matter if it rapidly goes back into the loop or not. – Jabari Lindsay Aug 04 '20 at 04:29
  • I've added a new answer, see below. – Orius Aug 04 '20 at 04:30
0

You only want to print those messages when the third for loop is in action, right? I can't run the code, since I don't have access to some resources, but hopefully this should work:

import threading
import sched, time
import numpy as np
import cv2
import imutils

flag = False

def printit():
    while (True):
        time.sleep(2)
        if (flag)
            print("Hello, world!")  

t = threading.Thread(target=printit)
t.start()

fullbody_cascade = cv2.CascadeClassifier('haarcascade_fullbody.xml') #creates variables for the different cascades
upperbody_cascade = cv2.CascadeClassifier('haarcascade_upperbody.xml')
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
cap0 = cv2.VideoCapture(0) #chooses camera to be utilized

while True:
    
    ret0, frame0 = cap0.read() #reads the frames of the chosen camera
    
    gray0 = cv2.cvtColor(frame0, cv2.COLOR_BGR2GRAY) #converts to grayscale
     
    fullbody0 = fullbody_cascade.detectMultiScale(gray0) #detects the grayscaled frame
    upperbody0 = upperbody_cascade.detectMultiScale(gray0)
    face0 = face_cascade.detectMultiScale(gray0)
     
    for (x,y,w,h) in fullbody0:
        cv2.rectangle(frame0, (x,y), (x+w, y+h), (0,0,255), 2) #creates red a box around entire body
        
    for (x,y,w,h) in upperbody0:
        cv2.rectangle(frame0, (x,y), (x+w, y+h), (255,0,0), 2) #creates a blue box around upper body
    
    flag = True
    for (x,y,w,h) in face0:
        cv2.rectangle(frame0, (x,y), (x+w, y+h), (0,255,0), 2) #creates a green box around face
    flag = False 
    
    cv2.imshow('cam0',frame0)
    k = cv2.waitKey(30) & 0xff
    if k == 27:
        break

cv2.destroyAllWindows()
Orius
  • 1,093
  • 5
  • 11
  • BTW, you should probably give this flag a more meaningful name. Maybe "print_messages"? – Orius Aug 04 '20 at 04:33
  • Tried this and nothing prints. – Jabari Lindsay Aug 04 '20 at 04:36
  • The code I've written works, but it prints over and over once it returns to the loop, I tried your method by putting t.start() within the while loop and still nothing prints, I'm not sure what to do at this point. – Jabari Lindsay Aug 04 '20 at 04:56
  • Maybe nothing prints because the for loop just finishes too quickly? To test this theory, you can add, say, time.sleep(70) between "flag = True" and the for loop. You shouldn't put t.start() inside a loop, you should only start the thread once. – Orius Aug 04 '20 at 05:11
  • When I add time.sleep(70) it prints "Hello, World!" every 2 seconds, but since it's sleeping the rest of the code won't start which loads up the camera. This results in me only getting 1 frame of the camera every 70 seconds. Also, it prints no matter what whether the condition is met or not. – Jabari Lindsay Aug 04 '20 at 05:22
  • I also want to mention the reasoning behind this, the print is supposed to be a text message sent to your phone, so obviously I don't want to simply put a print in the for loop because then it will continuously text the phone that's why I want it to text once every x seconds. I'm not sure how possible this is, is it possible to tell it "Print once but do not print again if condition is met for another 2 minutes" for example. – Jabari Lindsay Aug 04 '20 at 05:37
  • Obviously you don't want to leave that time.sleep(70) there, it was just for testing purposes. I guess the third for loop just finishes too quickly. Remove that time.sleep(70) and change time.sleep(2) in printit() to time.sleep(0.01) (again, for testing purposes, you'll change it back eventually). I'm not sure you can actually tell that it prints whether the condition is met or not, how can you know? – Orius Aug 04 '20 at 05:38
  • I can know by putting my finger on the camera therefore no detection can be done, and when I do that, "Hello, World!" still prints, I'm not sure why it may be the position of the flag going true, I think it will work if I make an if condition within the for loop like I had before. – Jabari Lindsay Aug 04 '20 at 05:45
  • Okay so I did what I said by putting the true flag within the for loop and it works as intended now, thank you for all the help! I will update the code with what works! You were right about letting it sleep, I have it sleep for 0.5 seconds and it works now. Thanks again! – Jabari Lindsay Aug 04 '20 at 06:04
  • I updated my code to what actually works, thank you for the help again! – Jabari Lindsay Aug 04 '20 at 06:09