【发布时间】:2020-08-22 15:47:09
【问题描述】:
在我的 GUI 应用程序中,我正在向用户显示摄像机流。现在的问题是,用户一次只能看到来自一台摄像机的流,并且为了看到来自其他摄像机的流,他必须输入新摄像机的凭据,例如 username、password 和 camera IP .
我想使用对话框来执行此操作。我能够做到这一点,但每次都会弹出一个新窗口。我如何使用QStackedLayout 在不同的相机之间切换,但这次我不能使用它,因为相机对象是在运行时创建的。
我只想在按下按钮时出现一个对话框,并且在输入凭据后必须更换相机。
代码:
from PyQt5 import QtCore, QtGui, QtWidgets
from threading import Thread
from collections import deque
from datetime import datetime
import time
import sys
import cv2
import imutils
class CameraWidget(QtWidgets.QWidget):
"""Independent camera feed
Uses threading to grab IP camera frames in the background
@param width - Width of the video frame
@param height - Height of the video frame
@param stream_link - IP/RTSP/Webcam link
@param aspect_ratio - Whether to maintain frame aspect ratio or force into fraame
"""
def __init__(self, username, password, camera_ip, width=0, height=0, stream_link=0, aspect_ratio=False, parent=None, deque_size=1):
super(CameraWidget, self).__init__(parent)
# Initialize deque used to store frames read from the stream
self.deque = deque(maxlen=deque_size)
# Slight offset is needed since PyQt layouts have a built in padding
# So add offset to counter the padding
self.screen_width = 640
self.screen_height = 480
self.maintain_aspect_ratio = aspect_ratio
self.camera_stream_link = 'rtsp://{}:{}@{}/Streaming/Channels/2'.format(username, password, camera_ip)
# Flag to check if camera is valid/working
self.online = False
self.capture = None
self.video_frame = QtWidgets.QLabel()
self.load_network_stream()
# Start background frame grabbing
self.get_frame_thread = Thread(target=self.get_frame, args=())
self.get_frame_thread.daemon = True
self.get_frame_thread.start()
# Periodically set video frame to display
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.set_frame)
self.timer.start(.5)
print('Started camera: {}'.format(self.camera_stream_link))
def load_network_stream(self):
"""Verifies stream link and open new stream if valid"""
def load_network_stream_thread():
if self.verify_network_stream(self.camera_stream_link):
self.capture = cv2.VideoCapture(self.camera_stream_link)
self.online = True
self.load_stream_thread = Thread(target=load_network_stream_thread, args=())
self.load_stream_thread.daemon = True
self.load_stream_thread.start()
def verify_network_stream(self, link):
"""Attempts to receive a frame from given link"""
cap = cv2.VideoCapture(link)
if not cap.isOpened():
return False
cap.release()
return True
def get_frame(self):
"""Reads frame, resizes, and converts image to pixmap"""
while True:
try:
if self.capture.isOpened() and self.online:
# Read next frame from stream and insert into deque
status, frame = self.capture.read()
if status:
self.deque.append(frame)
else:
self.capture.release()
self.online = False
else:
# Attempt to reconnect
print('attempting to reconnect', self.camera_stream_link)
self.load_network_stream()
self.spin(2)
self.spin(.001)
except AttributeError:
pass
def spin(self, seconds):
"""Pause for set amount of seconds, replaces time.sleep so program doesnt stall"""
time_end = time.time() + seconds
while time.time() < time_end:
QtWidgets.QApplication.processEvents()
def set_frame(self):
"""Sets pixmap image to video frame"""
if not self.online:
self.spin(1)
return
if self.deque and self.online:
# Grab latest frame
frame = self.deque[-1]
# Keep frame aspect ratio
if self.maintain_aspect_ratio:
self.frame = imutils.resize(frame, width=self.screen_width)
# Force resize
else:
self.frame = cv2.resize(frame, (self.screen_width, self.screen_height))
self.frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
h, w, ch = self.frame.shape
bytesPerLine = ch * w
# Convert to pixmap and set to video frame
self.img = QtGui.QImage(self.frame, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
self.pix = QtGui.QPixmap.fromImage(self.img)
self.video_frame.setPixmap(self.pix)
def get_video_frame(self):
return self.video_frame
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, username, password, camera_ip, parent=None):
super(MainWindow, self).__init__(parent)
# Top frame
self.top_frame = QtWidgets.QFrame()
self.top_frame.setStyleSheet("background-color: rgb(153, 187, 255)")
self.camera = CameraWidget(username, password, camera_ip)
self.top_layout = QtWidgets.QHBoxLayout()
self.top_layout.addWidget(self.camera.get_video_frame())
self.top_frame.setLayout(self.top_layout)
# Bottom frame
self.btm_frame = QtWidgets.QFrame()
self.btm_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
self.button = QtWidgets.QPushButton('Change Camera')
self.button.clicked.connect(self.onClick)
self.btm_layout = QtWidgets.QHBoxLayout()
self.btm_layout.addStretch()
self.btm_layout.addWidget(self.button)
self.btm_layout.setContentsMargins(5, 5, 5, 5)
self.btm_frame.setLayout(self.btm_layout)
self.widget = QtWidgets.QWidget()
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.top_frame, 20)
self.layout.addWidget(self.btm_frame,1)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
def onClick(self):
"""
I want this function to open a dialog box
asking user to enter new cameras credentials
and display it.
"""
if __name__ == '__main__':
# Create main application window
app = QtWidgets.QApplication([])
app.setStyle(QtWidgets.QStyleFactory.create("Fusion"))
w = MainWindow('admin', 'vaaan@123', '192.168.1.51')
w.showMaximized()
sys.exit(app.exec_())
【问题讨论】:
标签: python user-interface pyqt pyqt5