我想画一个圆圈,当按下鼠标时,该圆圈显示在视频上的光标位置。视频正在MainWindow中的QLabel对象中播放。我正在使用OpenCV以10 fps的速度从网络摄像头读取帧。我将帧转换为QPixmap,并在QLabel对象(self.vidWindow
)中显示它们。
在下面的代码中,当启动MainWindow时,圆圈立即被绘制(不是我想要的),然后被视频流覆盖。文本显示在遮罩Qlabel对象中,并在按下鼠标按钮时在MainWindow中显示一条消息。
我可以在QLabel对象中画一个圆吗?如果是这样,我应该使用遮罩QLabel对象还是可以直接在self.vidWindow
中的视频上覆盖?
在代码的最小化版本中,视频显示,但是当我尝试绘制椭圆时会触发错误。
import sys, cv2
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel
from PyQt5.QtGui import QImage, QPixmap, QPainter, QPen, QFont
from PyQt5.QtCore import QTimer, Qt, QCoreApplication
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.statusBar().showMessage('Ready')
self.setGeometry(50, 50, 800, 600)
self.setWindowTitle('Statusbar')
self.vidWindow = QLabel(self)
self.vidWindow.setGeometry(20, 20, 640, 480)
self.maskWindow = QLabel(self)
self.maskWindow.setGeometry(20, 20, 640, 480)
self.maskWindow.setStyleSheet('background-color: rgba(0,0,0,0%)')
font = QFont()
font.setPointSize(18)
font.setBold(True)
font.setWeight(75)
self.maskWindow.setFont(font)
self.maskWindow.setText('Message is on the mask Qlabel object')
self.msgLabel = QLabel(self)
self.msgLabel.setGeometry(675, 300, 100, 20)
self.cap = cv2.VideoCapture(0)
self.pix = QImage()
self.timer = QTimer()
self.frame_rate = 5
self.show()
self.start()
def nextFrameSlot(self):
ret, frame = self.cap.read()
if ret == True:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = QImage(frame,frame.shape[1], frame.shape[0], QImage.Format_RGB888)
img = img.scaled(640, 480, Qt.KeepAspectRatio)
self.pix = QPixmap.fromImage(img)
self.vidWindow.setPixmap(self.pix)
def mousePressEvent(self, QMouseEvent):
self.msgLabel.setText('Mouse Clicked!')
def paintEvent(self, QMouseEvent):
e = QMouseEvent
painter = QPainter(self)
painter.setPen(QPen(Qt.green, 4, Qt.SolidLine))
painter.drawEllipse(e.x(), e.y(), 100)
def start(self):
rate = int(1000.0 / self.frame_rate)
self.timer.setTimerType(Qt.PreciseTimer)
self.timer.timeout.connect(self.nextFrameSlot)
self.timer.start(rate)
def closeEvent(self, event):
if self.cap.isOpened():
self.cap.release()
self.vidWindow.clear()
QCoreApplication.quit()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())
尽管QPainter用于绘制窗口小部件,但在这种情况下将不起作用,因为它将其子级以下的“ MainWindow”绘制为QLabels。至少有两种可能的解决方案:
创建自定义QLabel并检测单击并绘制圆圈,
创建一个显示带有圆圈的QPixmap的QLabel,然后根据鼠标信息移动它。
在这种情况下,我将实现第二种方法:
import sys, cv2
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel
from PyQt5.QtGui import QImage, QPixmap, QPainter, QPen, QFont
from PyQt5.QtCore import QTimer, Qt
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.statusBar().showMessage("Ready")
self.setGeometry(50, 50, 800, 600)
self.setWindowTitle("Statusbar")
self.vidWindow = QLabel(self)
self.vidWindow.setGeometry(20, 20, 640, 480)
self.maskWindow = QLabel(self)
self.maskWindow.setGeometry(20, 20, 640, 480)
self.maskWindow.setStyleSheet("background-color: rgba(0,0,0,0%)")
font = QFont()
font.setPointSize(18)
font.setBold(True)
font.setWeight(75)
self.maskWindow.setFont(font)
self.maskWindow.setText("Message is on the mask Qlabel object")
self.msgLabel = QLabel(self)
self.msgLabel.setGeometry(675, 300, 100, 20)
self.marker_label = QLabel(self)
pixmap = QPixmap(100, 100)
pixmap.fill(Qt.transparent)
painter = QPainter(pixmap)
painter.setPen(QPen(Qt.green, 4, Qt.SolidLine))
painter.drawEllipse(pixmap.rect().adjusted(4, 4, -4, -4))
painter.end()
self.marker_label.setPixmap(pixmap)
self.marker_label.adjustSize()
self.marker_label.hide()
self.marker_label.raise_()
self.cap = cv2.VideoCapture(0)
self.timer = QTimer()
self.frame_rate = 5
self.show()
self.start()
def nextFrameSlot(self):
ret, frame = self.cap.read()
if ret == True:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
img = img.scaled(640, 480, Qt.KeepAspectRatio)
pix = QPixmap.fromImage(img)
self.vidWindow.setPixmap(pix)
def mousePressEvent(self, event):
self.msgLabel.setText("Mouse Clicked!")
if self.vidWindow.rect().contains(event.pos()):
self.marker_label.move(event.pos() - self.marker_label.rect().center())
self.marker_label.show()
super().mousePressEvent(event)
def start(self):
rate = int(1000.0 / self.frame_rate)
self.timer.setTimerType(Qt.PreciseTimer)
self.timer.timeout.connect(self.nextFrameSlot)
self.timer.start(rate)
def closeEvent(self, event):
if self.cap.isOpened():
self.cap.release()
super().closeEvent(event)
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())