Search code examples
pythonmultithreadingtimerinterrupt-handlinginterrupted-exception

How can I do something within a while loop at a particular interval without interrupting the entire loop?


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!


Solution

  • 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