首页 > 解决方案 > 线程应用程序不会因键盘中断 Python 而停止

问题描述

我想用键盘中断来停止线程化的 python 应用程序。一个线程捕获图像并将它们放入队列中,另一个线程将图像保存到硬盘。

代码在这里

import numpy as np
import threading
import time
from multiprocessing import Queue
import cv2
from datetime import datetime
import os
#import matplotlib.pyplot as plt
import sys


frames = Queue(50)
exitProgram = False
saveImages = True


class ImageGrabber(threading.Thread):
    def __init__(self, ID):
        threading.Thread.__init__(self)
        self.ID=ID
        self.cam=cv2.VideoCapture(ID)
        self.fps = self.cam.get(cv2.CAP_PROP_FPS)
        self.w=self.cam.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.h=self.cam.get(cv2.CAP_PROP_FRAME_HEIGHT)
        (self.grabbed, self.frame) =self.cam.read()
        cv2.imwrite("testImage.png",self.frame)
        print(f"Camera Opened with fps {self.fps}, width = {self.w}, and height = {self.h}")     
        
        self.stopped = False
        # self.adjustB = adjustBrightness(0.75)  

        
    
    def run(self):
        global frames
        global exitProgram
        while not self.stopped:
            if not self.grabbed or exitProgram is True:
                print("Exit Command reached")
                self.stop()
                self.cam.release()
            else:
                (self.grabbed, self.frame) =self.cam.read()               
                frames.put(self.frame)
                
                
    def stop(self):
        self.stopped = True


class imageSaveThread(threading.Thread):
    def __init__(self,grabber,filePath):
         
        threading.Thread.__init__(self)
        global saveImages
        self.dateTime = self.getDateStamp()
        self.imgName = filePath + 'img_' + self.dateTime + '_'
        self.cntr = 0        
        
    def getDateStamp(self):    
        filedate = str(datetime.now())
        filedate = filedate[0:-7]
        filedate = filedate.replace(':', '_')
        filename = filedate 
        return filename

    def run(self):
        global frames
        while True:
            if(not frames.empty()):
                self.Currframe=frames.get()
                cv2.imwrite(self.imgName + str(self.cntr).zfill(6) + '.png',self.Currframe)
                self.cntr = self.cntr + 1
                print(f"Queue Size in writing = {frames.qsize()} and fram number = {self.cntr}")
            elif exitProgram is True:
                print("Exit Command imageSaveThread reached")
                print(f"Final Queue Size at exit = {frames.qsize()}")
                break         
      

def main():
    if saveImages == True:
        savefilePath = 'D:/111/'
        grabber = ImageGrabber(0)
        imageSaveThr = imageSaveThread(grabber,savefilePath)        
        grabber.start()
        imageSaveThr.start()
        e = threading.Event()
        # imageSaveThread.join()
        # grabber.join()
        print ('Press CTRL-C to interrupt')
        while grabber.isAlive():
            try: 
                time.sleep(5) #wait 1 second, then go back and ask if thread is still alive
            except KeyboardInterrupt: #if ctrl-C is pressed within that second,
                                      #catch the KeyboardInterrupt exception
                e.set() #set the flag that will kill the thread when it has finished
                exitProgram=True
                print ('Exiting...')
                grabber.join()
                imageSaveThr.join()
          
    else:
        videoCodec = 'h264'
        videoExt = 'mkv'
    
if __name__ == '__main__':
    main()

II 替换主函数并直接调用程序然后它可以工作并退出线程,如下面的代码

import numpy as np
import threading
import time
from multiprocessing import Queue
import cv2
from datetime import datetime
import os
#import matplotlib.pyplot as plt
import sys


frames = Queue(50)
exitProgram = False
saveImages = True


class ImageGrabber(threading.Thread):
    def __init__(self, ID):
        threading.Thread.__init__(self)
        self.ID=ID
        self.cam=cv2.VideoCapture(ID)
        self.fps = self.cam.get(cv2.CAP_PROP_FPS)
        self.w=self.cam.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.h=self.cam.get(cv2.CAP_PROP_FRAME_HEIGHT)
        (self.grabbed, self.frame) =self.cam.read()
        cv2.imwrite("testImage.png",self.frame)
        print(f"Camera Opened with fps {self.fps}, width = {self.w}, and height = {self.h}")     
        
        self.stopped = False
        # self.adjustB = adjustBrightness(0.75)  

        
    
    def run(self):
        global frames
        global exitProgram
        while not self.stopped:
            if not self.grabbed or exitProgram is True:
                print("Exit Command reached")
                self.stop()
                self.cam.release()
            else:
                (self.grabbed, self.frame) =self.cam.read()               
                frames.put(self.frame)
                
                
    def stop(self):
        self.stopped = True


class imageSaveThread(threading.Thread):
    def __init__(self,grabber,filePath):
         
        threading.Thread.__init__(self)
        global saveImages
        self.dateTime = self.getDateStamp()
        self.imgName = filePath + 'img_' + self.dateTime + '_'
        self.cntr = 0        
        
    def getDateStamp(self):    
        filedate = str(datetime.now())
        filedate = filedate[0:-7]
        filedate = filedate.replace(':', '_')
        filename = filedate 
        return filename

    def run(self):
        global frames
        while True:
            if(not frames.empty()):
                self.Currframe=frames.get()
                cv2.imwrite(self.imgName + str(self.cntr).zfill(6) + '.png',self.Currframe)
                self.cntr = self.cntr + 1
                print(f"Queue Size in writing = {frames.qsize()} and fram number = {self.cntr}")
            elif exitProgram is True:
                print("Exit Command imageSaveThread reached")
                print(f"Final Queue Size at exit = {frames.qsize()}")
                break         
      

savefilePath = 'D:/111/'
grabber = ImageGrabber(0)
imageSaveThr = imageSaveThread(grabber,savefilePath)        
grabber.start()
imageSaveThr.start()
e = threading.Event()
# imageSaveThread.join()
# grabber.join()
print ('Press CTRL-C to interrupt')
while grabber.isAlive():
    try: 
        time.sleep(5) #wait 1 second, then go back and ask if thread is still alive
    except KeyboardInterrupt: #if ctrl-C is pressed within that second,
                              #catch the KeyboardInterrupt exception
        e.set() #set the flag that will kill the thread when it has finished
        exitProgram=True
        print ('Exiting...')
        grabber.join()
        imageSaveThr.join()

我无法理解原因。谁能告诉我是什么原因?

标签: pythonmultithreadingpython-multithreading

解决方案


PS(会添加这个作为评论,但我不能,因为我不符合要求)

我将把解释留给其他人,因为我无法让您的代码在我的计算机上运行。

但是您是否考虑过在 except 块的末尾添加一个 sys.exit(0) ?每当我尝试使用 KeyboarInterrupt 摆脱这个小代码片段时,它都会对我产生影响:

import sys

if __name__ == "__main__":
     while True:

         try:
             print("Hello")
         except KeyboardInterrupt:
             print("Exiting...")
             sys.exit(0)

PS:如果您最终删除了 sys.exit() 部分,请使用 Ctrl+Z 并手动终止该进程。


推荐阅读