如何在 qt 应用程序上使用 cv2.waitKey 暂停和播放 'p' 键
How to pause and play with 'p' key using cv2.waitKey on qt application
我正在使用下面的代码。
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' 键盘键时它没有暂停,它不起作用。请让我知道这是正确的方法。请告诉我解决方案。已编辑代码。
不要修改 Qt Designer 生成的 class,但必须将其导入主脚本,在这种情况下,您必须使用 pyuic5 your_design.ui -o gui.py -x
再次生成 .py。
如果显示 opencv 帧的 window 不是由 opencv 创建的,则不应使用 waitKey(),因为它不会处理键盘事件,也就是说,如果 window 是由X 技术和 opencv 仅用于从某些设备获取图像,那么 X 技术必须处理键盘事件。在这种情况下,技术就是 Qt。
这在the docs中指出:
Note: The function only works if there is at least one HighGUI window
created and the window is active. If there are several HighGUI
windows, any of them can be active.
另一方面,读取一帧的任务不会消耗太多时间,所以你不应该使用 while True 因为它会阻塞 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:
#
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_())
我正在使用下面的代码。
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' 键盘键时它没有暂停,它不起作用。请让我知道这是正确的方法。请告诉我解决方案。已编辑代码。
不要修改 Qt Designer 生成的 class,但必须将其导入主脚本,在这种情况下,您必须使用 pyuic5 your_design.ui -o gui.py -x
再次生成 .py。
如果显示 opencv 帧的 window 不是由 opencv 创建的,则不应使用 waitKey(),因为它不会处理键盘事件,也就是说,如果 window 是由X 技术和 opencv 仅用于从某些设备获取图像,那么 X 技术必须处理键盘事件。在这种情况下,技术就是 Qt。
这在the docs中指出:
Note: The function only works if there is at least one HighGUI window created and the window is active. If there are several HighGUI windows, any of them can be active.
另一方面,读取一帧的任务不会消耗太多时间,所以你不应该使用 while True 因为它会阻塞 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:
#
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_())