You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PyQt5中从实时视频流截取并保存图片?

解决PyQt5摄像头应用截图保存问题

问题分析

你的代码存在两个核心问题导致截图功能失效:

  • Take Snap按钮未绑定点击事件,点击后无任何响应逻辑
  • 错误使用isChecked()判断按钮状态,普通QPushButton默认无选中状态,该方法仅适用于复选框类控件

修复方案

  1. 添加截图标志位,标记是否需要保存当前视频帧
  2. 给Take Snap按钮绑定点击事件,触发时设置标志位为True
  3. 在视频帧更新的槽函数中检测标志位,完成截图后立即重置标志位,避免重复保存
  4. 可选优化:用时间戳命名截图文件防止覆盖旧文件,同时在界面展示截取的图片预览

完整修复代码

import sys
import time
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import cv2

class App(QWidget):
    def __init__(self):
        super(App, self).__init__()
        self.snap_flag = False  # 截图控制标志位

        # 主布局设置
        self.main_layout = QVBoxLayout()
        self.setLayout(self.main_layout)

        # 分组框
        self.groupbox = QGroupBox("Data Collector")
        self.groupbox.setCheckable(False)
        self.main_layout.addWidget(self.groupbox)

        # 分组框内水平布局
        self.groupbox_layout = QHBoxLayout()
        self.groupbox.setLayout(self.groupbox_layout)

        # 分组框内嵌套两个垂直布局
        self.button_layout = QVBoxLayout()
        self.feed_layout = QVBoxLayout()
        self.groupbox_layout.addLayout(self.button_layout)
        self.groupbox_layout.addLayout(self.feed_layout)

        # 添加按钮组件
        self.add_Buttons()

        # 摄像头工作线程
        self.worker = Worker(0)

        # 视频流显示标签
        self.img = QLabel()
        self.img.setAlignment(Qt.AlignTop)
        self.feed_layout.addWidget(self.img)


    def add_Buttons(self):
        # 创建三个水平布局
        self.hbox_3 = QHBoxLayout()
        self.hbox_3.setSpacing(10)
        self.hbox_3.setAlignment(Qt.AlignLeft)

        self.hbox_5 = QHBoxLayout()
        self.hbox_5.setSpacing(10)
        self.hbox_5.setAlignment(Qt.AlignLeft | Qt.AlignTop)

        self.hbox_6 = QHBoxLayout()
        self.hbox_6.setSpacing(50)
        self.hbox_6.setAlignment(Qt.AlignHCenter)


        # 启停按钮布局
        start_camera = QPushButton("Start Feed")
        stop_camera = QPushButton("Stop Feed")
        start_camera.setMaximumSize(200, 30)
        stop_camera.setMaximumSize(200, 30)
        start_camera.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        stop_camera.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        start_camera.clicked.connect(self.start_Feed)
        stop_camera.clicked.connect(self.stop_Feed)

        self.hbox_3.addWidget(start_camera)
        self.hbox_3.addWidget(stop_camera)
        self.button_layout.addLayout(self.hbox_3)


        # 截图按钮布局
        self.take_picture = QPushButton("Take Snap")
        self.take_picture.setMaximumSize(200,30)
        self.take_picture.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.take_picture.clicked.connect(self.take_Snap)  # 绑定点击事件

        self.hbox_5.addWidget(self.take_picture)
        self.button_layout.addLayout(self.hbox_5)

        # 截图预览布局
        self.clicked_image = QLabel()
        self.clicked_image.setAlignment(Qt.AlignTop)
        self.clicked_image.setFixedSize(160, 120)  # 固定预览尺寸

        self.hbox_6.addWidget(self.clicked_image)
        self.button_layout.addLayout(self.hbox_6)

    def start_Feed(self):
        self.worker.start()
        self.worker.image_update.connect(self.image_Update_Slot)

    def take_Snap(self):
        self.snap_flag = True  # 触发截图标志

    def image_Update_Slot(self, image):
        self.img.setPixmap(QPixmap.fromImage(image))
        
        if self.snap_flag:
            # 用时间戳命名文件,避免覆盖
            timestamp = int(time.time())
            save_path = f"snap_{timestamp}.png"
            image.save(save_path)
            print(f"截图已保存至: {save_path}")
            
            # 显示截图预览
            preview_img = image.scaled(160, 120, Qt.KeepAspectRatio)
            self.clicked_image.setPixmap(QPixmap.fromImage(preview_img))
            
            self.snap_flag = False  # 重置标志位,防止重复保存

    def stop_Feed(self):
        self.worker.stop()


class Worker(QThread):
    image_update = pyqtSignal(QImage)
    def __init__(self, n):
        super(Worker, self).__init__()
        self.n = n
    def run(self):
        self.thread_active = True
        capture = cv2.VideoCapture(self.n, cv2.CAP_V4L)
        while self.thread_active:
            ret, frame = capture.read()
            if ret:
                img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                flipped_img = cv2.flip(img,1)
                to_qt = QImage(flipped_img.data, flipped_img.shape[1], flipped_img.shape[0], QImage.Format_RGB888)
                pic = to_qt.scaled(640, 480, Qt.KeepAspectRatio)
                self.image_update.emit(pic)

    def stop(self):
        self.thread_active = False
        self.quit()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    root = App()
    root.show()
    sys.exit(app.exec())

关键改动说明

  • 新增self.snap_flag标志位,精准控制截图时机
  • 给Take Snap按钮绑定点击事件,触发时设置标志位
  • 在帧更新槽函数中完成截图逻辑,保存后立即重置标志位
  • 添加时间戳命名规则,避免文件覆盖
  • 新增截图预览功能,在界面右侧小标签展示截取的图片

内容的提问来源于stack exchange,提问作者sb72

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 04:50:23