在我的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_())
初步答复
这和你的想法接近吗
如果看不到什么东西,很难判断是否有什么东西丢失了,但这应该是正确的方向
答复意见
关于必填字段
首先,这个建议很粗糙
您应该在每次QLineEdit之前添加QLabel
然后,您应该编写一些验证逻辑。您可以通过删除我放入的默认“OK”按钮并放入您自己的按钮来完成此操作。按下此按钮时,检查每个输入
self
(对话框)是否有效如果是这种情况,您可以调用^{} 。否则,您可以对无效的第一个输入使用^{}
显示以前输入的数据
在我的提案中,我创建了一个与主窗口一起存储的对话框
它从未被销毁,因此所有数据仍然有效。当您第二次显示该对话框时,它仍然保存以前的数据
如果愿意,您可以每次创建一个新的对话框对象,或者清除所有输入
相关问题 更多 >
编程相关推荐