Home > Software engineering >  How should I properly stop a Thread?
How should I properly stop a Thread?

Time:09-20

I am making a simple GUI for a video capture application using customtkinter and opencv. The code below is the creation and start of the thread which is responsible for executing the video capture function. This code is inside the initialization of my application class:

class App(customtkinter.CTk):
    def __init__(self):
       ...
       self.stopEvent = threading.Event()
       self.thread = threading.Thread(target=self.videoLoop, args=())
       self.thread.start()
       ...

the function for the actual video capturing is:

def videoLoop(self):
    while True:
        if self.stopEvent.is_set() == True:
            cap.release()
            cv2.destroyAllWindows()
            break
        ret, frame = cap.read()
        img = cv2.rectangle(frame, (380, 80), (1540,950), (0,0,255),2)
        #add vertical line
        img2 = cv2.line(img, (960, 85), (960,945), (0,255,0),1)
        #add horizontal line
        img3 = cv2.line(img2, (400, 515), (1520,515), (0,255,0),1)
        #get frame rate
        fps = int(cap.get(cv2.CAP_PROP_FPS))
        #add frame rate text
        disp_frame = cv2.putText(img3, "FPS: "  str(fps), (0, 50), cv2.FONT_HERSHEY_PLAIN, 4, (100, 255, 0), 2)
        conv_img = cv2.cvtColor(disp_frame, cv2.COLOR_BGR2RGB)
        conv_img = Image.fromarray(conv_img)
        conv_img = ImageTk.PhotoImage(image=conv_img)
        self.videoframe.create_image(500,500,image=conv_img,anchor="s")
        self.videoframe.image = conv_img

upon exiting the application, the on_closing is executed for the clean-up:

def on_closing(self):
    self.stopEvent.set()
    self.destroy()

The issue with this one is that when I try to exit the app, the thread that handles the video capturing seems to not terminate properly as I cannot interact with my VSCode terminal requiring me to run a new instance of the terminal before I can run my application again. Is there a specific way to stop the thread?

CodePudding user response:

Maybe you are perceiving a runtime issue. When this gets executed:

def on_closing(self):
    self.stopEvent.set()
    self.destroy()

Is it possible that self got detroyed before the other thread had a chance to properly finish?

In that case do not destroy the object immediately. Wait for a signal from the background thread that it has stopped processing, then run destroy.

CodePudding user response:

I found a workaround for the thread issue. Instead of initializing and starting the thread inside the App class, I created it inside the top-level code which now looks like this (btw, I renamed the videoLoop function into thread_loop:

if __name__ == "__main__":
    app = App()
    thread = threading.Thread(target=app.thread_loop,args=())
    thread.start()
    app.mainloop()
    thread.join()

and my on_closing function

def on_closing(self):
    cap.release()
    self.destroy()

I'm not sure if this is best solution but the thread now terminates properly

  • Related