我想通过使用网络摄像头将捕获图像添加到我的 PyQt gui window 并且网络摄像头图像将出现在 gui window

I wanted to add capturing image by using webcam to my PyQt gui window and webcam image will come on gui window

我想将网络摄像头图像添加到我的主 GUI window,该图像将发送到电子邮件 ID。如果这不可能,我还想保存该图像,保存的图像将发送到我的电子邮件 ID,在 3、2、1 倒计时时,微笑它会通过网络摄像头点击图像。 这是我的代码:

import sys
from PyQt5 import QtCore
from PyQt5 import QtWidgets, QtGui
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import cv2, time

DURATION_INT = 5


class MyMainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.time_left_int = DURATION_INT
        self.widget_counter_int = 0

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        vbox = QtWidgets.QVBoxLayout()
        central_widget.setLayout(vbox)

        self.pages_qsw = QtWidgets.QStackedWidget()
        vbox.addWidget(self.pages_qsw)
        self.time_passed_qll = QtWidgets.QLabel()
        vbox.addWidget(self.time_passed_qll)
        self.pushButton = QtWidgets.QPushButton()
        self.pushButton.setText("Push to start")
        self.yesbutton = QtWidgets.QToolButton()
        self.yesbutton.setText("yes")
        self.Nobutton = QtWidgets.QToolButton()
        self.Nobutton.setText("No")

        self.imageframe = QtWidgets.QLabel()
        self.imageframe.setText("fghkfhh")

        vbox.addWidget(self.Nobutton)
        vbox.addWidget(self.yesbutton)
        vbox.addWidget(self.pushButton)
        vbox.addWidget(self.imageframe)


        self.pushButton.clicked.connect(self.timer_start)
        self.yesbutton.clicked.connect(self.capturing_image)
        self.update_gui()

    def gmail_alert(self):
        email_user = 'user email_id'
        email_send = 'receiver email_id'

        subject = 'Alert system'

        msg = MIMEMultipart()
        msg['From'] = email_user
        msg['To'] = email_send
        msg['Subject'] = subject
        msg.preamble = "test"

        body = 'Hi there, sending this email from Python!'
        msg.attach(MIMEText(body, 'plain'))

        filename = 'alert.png'
        attachment = open(filename, 'rb')

        part = MIMEBase('application', 'octet-stream')
        part.set_payload((attachment).read())
        encoders.encode_base64(part)
        part.add_header('Content-Disposition', "attachment; 
                           filename= " + filename)

        msg.attach(part)
        text = msg.as_string()

        server = smtplib.SMTP('smtp.gmail.com', 587)
        server.starttls()
        server.login(email_user, 'user email_id password')
        server.sendmail(email_user, email_send, text)
        server.quit()

    def timer_start(self):
        self.time_left_int = DURATION_INT
        self.my_qtimer = QtCore.QTimer(self)
        self.my_qtimer.timeout.connect(self.timer_timeout)
        self.my_qtimer.start(1000)
        self.update_gui()

    def timer_timeout(self):
        if self.time_left_int > 0:
            self.time_left_int -= 1
        else:
            self.gmail_alert()
        self.update_gui()



    def update_gui(self):
        self.time_passed_qll.setText((str(self.time_left_int) if self.time_left_int >=1 else "Smile..!"))

    def capturing_image(self):
        video =cv2.VideoCapture(0)
        check, frame = video.read()

        print(check)
        print(frame)

        cv2.imshow("capturing", frame)
        video.release()

app = QtWidgets.QApplication(sys.argv)
main_window = MyMainWindow()
main_window.show()
sys.exit(app.exec_()

首先,您不必在 PyQt 中使用 cv2.imshow(),因为它会阻止 python 事件循环,如果您想在 PyQt 中显示 opencv 的图像,则必须将其转换为QImage或者QPixmap,接下来class实现opencv的数据获取,可以获取QImage,但是必须在线程中执行。

OpencvQt.py

import cv2
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets


class Capture(QtCore.QObject):
    started = QtCore.pyqtSignal()
    frameReady = QtCore.pyqtSignal(np.ndarray)

    def __init__(self, parent=None):
        super(Capture, self).__init__(parent)
        self._frame = None
        self.m_timer = QtCore.QBasicTimer()
        self.m_videoCapture = cv2.VideoCapture()

    @QtCore.pyqtSlot()
    def start(self, cam=0):
        if self.m_videoCapture is not None:
            self.m_videoCapture.release()
            self.m_videoCapture = cv2.VideoCapture(cam)
        if self.m_videoCapture.isOpened():
            self.m_timer.start(0, self)
            self.started.emit()

    @QtCore.pyqtSlot()
    def stop(self):
        self.m_timer.stop()

    def __del__(self):
        self.m_videoCapture.release()

    def frame(self):
        return self.m_frame

    def timerEvent(self, event):
        if event.timerId() != self.m_timer.timerId():
            return

        ret, val = self.m_videoCapture.read()
        if not ret:
            self.m_timer.stop()
            return
        self.m_frame = val    
        self.frameReady.emit(self.m_frame)

    frame = QtCore.pyqtProperty(np.ndarray, fget=frame, notify=frameReady, user=True)

class Converter(QtCore.QObject):
    imageReady = QtCore.pyqtSignal(QtGui.QImage)

    def __init__(self, parent=None):
        super(Converter, self).__init__(parent)
        self.m_frame = np.array([])
        self.m_timer = QtCore.QBasicTimer()
        self.m_processAll = True
        self.m_image = QtGui.QImage()

    def queue(self, frame):
        self.m_frame = frame
        if not self.m_timer.isActive():
            self.m_timer.start(0, self)

    def process(self, frame):
        w, h, _ = frame.shape
        rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        self.m_image = QtGui.QImage(rgbImage.data, h, w, QtGui.QImage.Format_RGB888)
        self.imageReady.emit(QtGui.QImage(self.m_image))

    def timerEvent(self, event):
        if event.timerId() != self.m_timer.timerId():
            return
        self.process(self.m_frame)
        self.m_timer.stop()

    def processAll(self):
        return self.m_processAll

    def setProcessAll(self, _all):
        self.m_processAll = _all

    def processFrame(self, frame):
        if self.m_processAll:
            self.process(frame)
        else:
            self.queue(frame)

    def image(self):
        return self.m_image

    image = QtCore.pyqtProperty(QtGui.QImage, fget=image, notify=imageReady, user=True)
    processAll = QtCore.pyqtProperty(bool, fget=processAll, fset=setProcessAll)

通过上面我们可以在 QLabel 中显示相机,另一方面我们必须将 QImage 转换为字节,我们使用 QByteArray 和 QBuffer。出现的另一个问题是电子邮件的发送需要一段时间,因此 GUI 可能会被阻塞,因此它必须在线程中执行。最后我添加了一个 QDialog,您必须在其中输入邮件数据。

main.py

import sys
import threading
from PyQt5 import QtCore, QtGui, QtWidgets

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders

from OpencvQt import Capture, Converter


config = {
    "DURATION_INT": 5
}

def send_email(user, pwd, recipient, subject, body, image_payload):
    msg = MIMEMultipart()
    msg['From'] = user
    msg['To'] = recipient
    msg['Subject'] = subject
    msg.attach(MIMEText(body, 'plain'))

    part = MIMEBase('application', 'octet-stream')

    part.set_payload(image_payload)
    encoders.encode_base64(part)
    filename = QtCore.QDateTime.currentDateTime().toString()+ '.png'
    part.add_header('Content-Disposition', "attachment; filename= " + filename)
    msg.attach(part)
    text = msg.as_string()

    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.starttls()
    server.login(user, pwd)
    server.sendmail(user, recipient, text)
    server.quit()

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)

        lay = QtWidgets.QVBoxLayout(central_widget)
        self.view = QtWidgets.QLabel()
        self.btn_start = QtWidgets.QPushButton("Start")
        self.btn_stop = QtWidgets.QPushButton("Stop")
        self.btn_send = QtWidgets.QPushButton("Send Email")
        self.label_time = QtWidgets.QLabel()
        lay.addWidget(self.view, alignment=QtCore.Qt.AlignCenter)
        lay.addWidget(self.btn_start)
        lay.addWidget(self.btn_stop)
        lay.addWidget(self.btn_send)
        lay.addWidget(self.label_time, alignment=QtCore.Qt.AlignCenter)
        self.view.setFixedSize(640, 400)
        self.show()
        self.init_camera()
        self.init_email()

    def init_camera(self):
        self.capture = Capture()
        self.converter = Converter()
        captureThread = QtCore.QThread(self)
        converterThread = QtCore.QThread(self)
        self.converter.setProcessAll(False)
        captureThread.start()
        converterThread.start()
        self.capture.moveToThread(captureThread)
        self.converter.moveToThread(converterThread)
        self.capture.frameReady.connect(self.converter.processFrame)
        self.converter.imageReady.connect(self.setImage)
        self.capture.started.connect(lambda: print("started"))
        self.btn_start.clicked.connect(self.capture.start)
        self.btn_stop.clicked.connect(self.capture.stop)

    @QtCore.pyqtSlot(QtGui.QImage)
    def setImage(self, image):
        self.view.setPixmap(QtGui.QPixmap.fromImage(image))

    def init_email(self):
        timeline = QtCore.QTimeLine(config["DURATION_INT"]*1000, self)
        timeline.frameChanged.connect(self.onFrameChanged)
        timeline.setFrameRange(0, config["DURATION_INT"])
        timeline.setDirection(QtCore.QTimeLine.Backward)
        self.btn_send.clicked.connect(timeline.start)

        d = EmailDialog(self)
        if d.exec_() == EmailDialog.Accepted:
            self._info = d.get_data()

    def onFrameChanged(self, frame):
        if frame !=0:
            self.label_time.setNum(frame)
        else:
            self.label_time.setText("Smile...!")
            QtWidgets.QApplication.beep()
            image = QtGui.QImage(self.converter.image)
            ba = QtCore.QByteArray()
            buff = QtCore.QBuffer(ba)
            image.save(buff, "PNG")
            th = threading.Thread(target=send_email, args=(*self._info, ba))
            th.start()

    def closeEvent(self, event):
        self.capture.stop()
        super(MainWindow, self).closeEvent(event)


class EmailDialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(EmailDialog, self).__init__(parent)
        lay = QtWidgets.QFormLayout(self)
        self.from_le = QtWidgets.QLineEdit()
        self.pass_le = QtWidgets.QLineEdit(echoMode=QtWidgets.QLineEdit.Password)
        self.to_le = QtWidgets.QLineEdit()
        self.subject_le = QtWidgets.QLineEdit()
        self.body_te = QtWidgets.QTextEdit()

        self.buttonBox = QtWidgets.QDialogButtonBox()
        self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

        lay.addRow("From: ", self.from_le)
        lay.addRow("Password: ", self.pass_le)
        lay.addRow("To: ", self.to_le)
        lay.addRow("Subject: ", self.subject_le)
        lay.addRow("Body: ", self.body_te)
        lay.addRow(self.buttonBox)

        self.from_le.textChanged.connect(self.enable_button)
        self.pass_le.textChanged.connect(self.enable_button)
        self.to_le.textChanged.connect(self.enable_button)
        self.enable_button()

    def enable_button(self):
        disabled = self.from_le.text() == "" or self.pass_le.text() == "" or self.to_le.text() == ""
        self.buttonBox.setDisabled(disabled)

    def get_data(self):
        return self.from_le.text(), self.pass_le.text(), self.to_le.text(), self.subject_le.text(), self.body_te.toPlainText()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    sys.exit(app.exec_())