Не изменяйте класс, сгенерированный 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_())