关闭PyQt应用后Webcam实例未释放的问题解决咨询
嘿,这个问题我之前也踩过坑!核心问题出在摄像头的捕获循环阻塞了PyQt的主线程——你点击按钮后,hello()函数里的while True直接把主线程卡死了,PyQt的UI事件(包括窗口关闭)根本没法及时处理。而且摄像头资源是hello()里的局部变量,窗口关闭时那个循环还没跑完,camera.release()根本没机会执行,所以摄像头一直被占着不放。
下面给你一套完整的解决方案:
核心思路
- 把摄像头操作移到独立的Qt线程中,避免阻塞主线程(PyQt的UI必须在主线程运行,阻塞会导致窗口假死)
- 封装摄像头控制类,提供启动、停止的接口,方便主动释放资源
- 在窗口的
closeEvent中先停止摄像头,等资源完全释放后再确认退出
修改后的完整代码
1. 封装摄像头线程类(可单独存为Webcam.py)
import cv2 from PyQt5.QtCore import QThread, pyqtSignal class WebcamThread(QThread): def __init__(self, parent=None): super().__init__(parent) self.camera = None self.is_running = False def run(self): self.is_running = True # 初始化摄像头 self.camera = cv2.VideoCapture(0) top, right, bottom, left = 10, 350, 225, 590 while self.is_running and self.camera.isOpened(): success, frame = self.camera.read() if not success: break # 绘制ROI区域 clone = frame.copy() cv2.rectangle(clone, (left, top), (right, bottom), (0, 255, 0), 2) cv2.imshow("Video Feed", clone) # 监听Q键退出,同时留足时间让线程响应停止信号 keypress = cv2.waitKey(1) & 0xFF if keypress == ord("q"): self.stop() break # 线程结束时自动清理资源 self.cleanup() def stop(self): # 设置标志位,让循环退出 self.is_running = False def cleanup(self): # 释放摄像头资源 if self.camera and self.camera.isOpened(): self.camera.release() # 销毁OpenCV的窗口 cv2.destroyAllWindows()
2. 修改PyQt应用代码(Application.py)
from PyQt5 import QtCore, QtGui, QtWidgets import sys class Ui_MainWindow(object): def setupUi(self, MainWindow): self.window = MainWindow self.window.setObjectName("MainWindow") self.window.resize(800, 600) self.centralwidget = QtWidgets.QWidget(self.window) self.centralwidget.setObjectName("centralwidget") # 调整按钮位置,方便显示 self.startButton = QtWidgets.QPushButton(self.centralwidget) self.startButton.setGeometry(QtCore.QRect(350, 250, 100, 30)) self.startButton.setText("&Start") self.window.setCentralWidget(self.centralwidget) QtCore.QMetaObject.connectSlotsByName(MainWindow) class MyWindow(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MyWindow, self).__init__(parent) self.setupUi(self) self.webcam_thread = None # 绑定按钮点击事件 self.startButton.clicked.connect(self.toggle_camera) def toggle_camera(self): # 切换摄像头启停状态 if self.webcam_thread is None or not self.webcam_thread.isRunning(): self.webcam_thread = WebcamThread(self) # 线程结束后重置按钮状态 self.webcam_thread.finished.connect(lambda: self.startButton.setText("&Start")) self.webcam_thread.start() self.startButton.setText("&Stop") else: self.webcam_thread.stop() self.startButton.setText("&Start") def closeEvent(self, event): # 窗口关闭前的确认逻辑 result = QtWidgets.QMessageBox.question( self, "Confirm Exit...", "Are you sure you want to exit?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No ) if result == QtWidgets.QMessageBox.Yes: # 先停止摄像头线程,等待资源释放 if self.webcam_thread and self.webcam_thread.isRunning(): self.webcam_thread.stop() self.webcam_thread.wait() # 等待线程完全结束 event.accept() else: event.ignore() if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) w = MyWindow() w.show() sys.exit(app.exec_())
关键改动说明
- 线程隔离:用
QThread把摄像头逻辑从主线程分离,保证UI始终响应流畅,窗口关闭事件能正常触发 - 可控循环:用
is_running标志代替while True,可以主动停止循环,避免无法中断的问题 - 主动清理:在
closeEvent中先停止摄像头线程,等待它执行cleanup()释放资源后再退出应用 - 用户体验优化:把按钮改成启停切换,不用重复点击,线程结束后自动重置按钮状态
内容的提问来源于stack exchange,提问作者Meta Overflow
相关产品推荐
相关产品推荐

