Как сделать паузу и играть с клавишей 'p', используя cv2.waitKey в приложении qt - PullRequest
2 голосов
/ 16 апреля 2020

Я использую приведенный ниже код.

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")      
        MainWindow.resize(640, 480)     
        self.centralwidget = QtWidgets.QWidget(MainWindow)      
        self.centralwidget.setObjectName("centralwidget")   
        self.label = QtWidgets.QLabel(self.centralwidget)   
        self.label.setGeometry(QtCore.QRect(10, 10, 500, 300))  
        self.label.setText("")  
        self.label.setObjectName("label")   
        self.pushButton = QtWidgets.QPushButton(self.centralwidget) 
        self.pushButton.setGeometry(QtCore.QRect(50, 400, 75, 23))  
        self.pushButton.setObjectName("pushButton") 
        MainWindow.setCentralWidget(self.centralwidget) 
        self.statusbar = QtWidgets.QStatusBar(MainWindow)   
        self.statusbar.setObjectName("statusbar")   
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        self.pushButton.clicked.connect(self.play)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))


    def play(self):     
        cap = cv2.VideoCapture('vtest.asf')
        while True:
            ret, show = cap.read()
            key = cv2.waitKey(1) & 0xFF
            if ret:
                rgbImage = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
                image = QImage(rgbImage.data, show.shape[1], show.shape[0], show.strides[0], QImage.Format_RGB888)
                l = self.label.setPixmap(QPixmap.fromImage(image).scaled(500, 300, Qt.IgnoreAspectRatio))
            if key == ord('p'):
                cv2.waitKey(0)

            elif key == ord('q'):
                break


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

Он показывается кадр за кадром, но не останавливается, когда я использую клавишу «p», что он не работает. Пожалуйста, дайте мне знать, что это правильный путь. И, пожалуйста, покажи мне решение. Отредактированный код.

1 Ответ

2 голосов
/ 16 апреля 2020

Не изменяйте класс, сгенерированный Qt Designer, но вы должны импортировать его в ваш основной скрипт, в этом случае вы должны снова сгенерировать .py, используя pyuic5 your_design.ui -o gui.py -x.


Вы не должны использовать waitKey (), если окно, которое показывает фрейм opencv, не создано opencv, поскольку оно не будет обрабатывать события клавиатуры, то есть, если окно генерируется технологией X, а opencv служит только для получения изображения из какое-то устройство, тогда технология X должна обрабатывать события клавиатуры. И в этом случае эта технология - Qt.

. Это указано в документах :

Примечание: Функция работает только если создано хотя бы одно окно High GUI и оно активно. Если существует несколько HighGUI windows, любой из них может быть активным.

С другой стороны, задача чтения кадра не занимает много времени, поэтому не следует использовать некоторое время True поскольку он блокирует eventl oop из GUI, но достаточно таймера (в случае Qt вы должны использовать QTimer).

Учитывая вышеизложенное, решение:

├── gui.py
└── main.py

main.py

from gui import Ui_MainWindow

from PyQt5 import QtCore, QtGui, QtWidgets

import cv2


class CameraManager(QtCore.QObject):
    frameChanged = QtCore.pyqtSignal(QtGui.QImage)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._capture = None
        self._interval = 30
        self._timer = QtCore.QTimer(
            self, interval=self._interval, timeout=self._on_timeout
        )

    @property
    def capture(self):
        return self._capture

    @capture.setter
    def capture(self, c):
        is_active = self._timer.isActive()
        if is_active:
            self._timer.stop()
        if self.capture is not None:
            self.capture.release()
        self._capture = c
        if is_active:
            self._timer.start()

    @property
    def interval(self):
        return self._interval

    @interval.setter
    def interval(self, t):
        is_active = self._timer.isActive()
        if is_active:
            self._timer.stop()
        self._timer.setInterval(t)
        if is_active:
            self._timer.start()

    @property
    def is_active(self):
        return self._timer.isActive() and self.capture is not None

    @QtCore.pyqtSlot()
    def start(self):
        self._timer.start()

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

    @QtCore.pyqtSlot()
    def _on_timeout(self):
        if self.capture is None:
            return
        ret, frame = self.capture.read()
        if ret:
            # https://stackoverflow.com/a/55468544/6622587
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            h, w, ch = frame.shape
            bytesPerLine = ch * w
            qImg = QtGui.QImage(
                frame.data, w, h, bytesPerLine, QtGui.QImage.Format_RGB888
            )
            self.frameChanged.emit(qImg)


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)

        self.camera_manager = CameraManager()
        self.camera_manager.frameChanged.connect(self.on_frame_changed)

        self.camera_manager.capture = cv2.VideoCapture("vtest.asf")

        self.pushButton.clicked.connect(self.camera_manager.start)

        QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_P), self, activated=self.on_p_pressed
        )

    @QtCore.pyqtSlot(QtGui.QImage)
    def on_frame_changed(self, image):
        pixmap = QtGui.QPixmap.fromImage(image)
        self.label.setPixmap(pixmap)

    @QtCore.pyqtSlot()
    def on_p_pressed(self):
        if self.camera_manager.is_active:
            self.camera_manager.stop()
        else:
            self.camera_manager.start()


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())
...