【问题标题】:Trouble in retrieving window size and widget size检索窗口大小和小部件大小时遇到​​问题
【发布时间】:2020-11-22 14:49:02
【问题描述】:

这是摄像头监控系统的代码。在configuration_page.py 文件中,尽管使用了self.showMaximized()resizeEvent(),但我得到的窗口大小为100x30。其中的小部件也显示为 640x480。我对在config.py 文件中的CameraDisplay 类中检索名为self.mid_frame 的小部件的大小特别感兴趣。

之前我在dashboard.py 文件中遇到了同样的问题,但在这种情况下我通过显式传递宽度和高度找到了一种解决方法。但是这次我被卡住了,因为我需要获取名为self.mid_frame 的小部件的大小。我不明白为什么连 resizeEvent 都显示错误的大小。

main.py

from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import Qt, QRect
from threading import Thread, RLock, Lock
from collections import deque
from datetime import datetime
import time
import sys
import cv2
import imutils
from dashboard import *
from configuration_page import *
from global_widgets import *


class CameraWidget(QtGui.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, stream_link=0, stacked_widget=None, width=0, height=0, btn_text=None, idx=None, 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)
        
        self.maintain_aspect_ratio = aspect_ratio
        self.camera_stream_link = stream_link
        self.stacked_widget = stacked_widget
        self.idx = idx

        # Flag to check if camera is valid/working
        self.online = False
        self.capture = None

        self.video_frame = QtGui.QLabel()
        self.video_frame_1 = QtGui.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:
            QtGui.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]
            frame_1 = self.deque[-1]

            # Display frames on dashboard and configuration pages
            # Frame for dashboard
            # 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

            # Frame for configuration page-----------------------------------------------------------------------------------------

                # print('2: ', self.screen_width_1, self.screen_height_1)
                self.frame_1 = cv2.resize(frame_1, (self.screen_width_1, self.screen_height_1))
                self.frame_1 = cv2.cvtColor(self.frame_1, cv2.COLOR_BGR2RGB)
                h_1, w_1, ch_1 = self.frame_1.shape
                bytesPerLine_1 = ch_1 * w_1

            # 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)

            self.img_1 = QtGui.QImage(self.frame_1, w_1, h_1, bytesPerLine_1, QtGui.QImage.Format_RGB888)
            self.pix_1 = QtGui.QPixmap.fromImage(self.img_1)                                             
            self.video_frame_1.setPixmap(self.pix_1)

    def set_frame_params(self, width, height, btn_text=None, idx=0):
        self.screen_width = width
        self.screen_height = height
        self.btn_text = btn_text
        self.idx = idx

    def set_frame_params_1(self, width, height):
        self.screen_width_1 = width
        self.screen_height_1 = height

    def get_video_display_frame(self):
        self.video_display_frame = QtGui.QFrame()
        self.video_layout = QtGui.QVBoxLayout()
        self.video_btn = QtGui.QPushButton(self.btn_text)
        self.video_btn.setStyleSheet("background-color: rgb(128, 159, 255);")
        self.video_btn.clicked.connect(self.on_clicked)
        # self.video_frame = QtGui.QLabel()
        self.video_frame.setScaledContents(True)
        self.video_layout.addWidget(self.video_btn)
        self.video_layout.addWidget(self.video_frame)
        self.video_layout.setContentsMargins(0,0,0,0)
        self.video_layout.setSpacing(0)
        self.video_display_frame.setLayout(self.video_layout)

        return self.video_display_frame

    def get_video_frame(self):
        self.video_frame_1.setScaledContents(True)
        return self.video_frame_1

    @QtCore.pyqtSlot()
    def on_clicked(self):
        GlobalObject().dispatchEvent("hello", args=(self.idx,))
        self.stacked_widget.setCurrentIndex(1)


class Window(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        self.stacked_widget = QtGui.QStackedWidget()

        layout = QtGui.QVBoxLayout()
        layout.setContentsMargins(0,0,0,0)
        layout.addWidget(self.stacked_widget)

        widget = QtGui.QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

        self.cam1 = CameraWidget(camera1, self.stacked_widget, idx=1)

        widget_1 = DashBoard(self.cam1, self.stacked_widget)
        widget_2 = ZoneConfig(self.cam1, self.stacked_widget)

        self.stacked_widget.addWidget(widget_1)
        self.stacked_widget.addWidget(widget_2)

        self.showMaximized()
        

camera1 = '../streams/Fog.avi'

if __name__ == '__main__':
    app = QtGui.QApplication([])
    app.setStyle(QtGui.QStyleFactory.create("plastique"))
    # app.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))

    window = Window()
    window.show()
    sys.exit(app.exec_())

dashboard.py

from PyQt4 import QtGui, QtCore
from global_widgets import *


class DashBoard(QtGui.QWidget):    
    def __init__(self, cam1, stacked_widget, parent=None):
        super(DashBoard, self).__init__(parent)

        self.showMaximized()

        self.screen_width = self.width()
        self.screen_height = self.height()

        self.stacked_widget = stacked_widget

        # Layouts and frames
        layout = QtGui.QVBoxLayout()

        top_frame = QtGui.QFrame()
        top_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
        mid_frame = QtGui.QFrame()   
        mid_frame.setStyleSheet("background-color: rgb(153, 187, 255)")

        layout.addWidget(top_frame, 1)
        layout.addWidget(QHLine())
        layout.addWidget(mid_frame, 20)
        layout.setContentsMargins(0,0,0,0)
        layout.setSpacing(0)
        self.setLayout(layout)
        
        # Top frame
        label = QtGui.QLabel('Dashboard')
        label.setFont(QtGui.QFont('Times New Roman', 20))

        top_layout = QtGui.QHBoxLayout()
        top_layout.addWidget(label, alignment=QtCore.Qt.AlignCenter)
        top_layout.setContentsMargins(5,5,5,5)
        top_frame.setLayout(top_layout)

        # Middle frame
        self.mid_layout = QtGui.QStackedLayout()

        # Create camera widgets
        print('Creating Camera Widgets...')

        cam_widget = Cam1(cam1, self.screen_width, self.screen_height, self)
        self.mid_layout.addWidget(cam_widget)
        self.mid_layout.setCurrentWidget(cam_widget)
        mid_frame.setLayout(self.mid_layout)  


class Cam1(QtGui.QWidget):
    def __init__(self, cam1, screen_width, screen_height, parent=None):
        super(Cam1, self).__init__(parent)

        cam1.set_frame_params(screen_width, screen_height, 'VIDS 01', 1)

        # Add widgets to layout
        print('Adding widgets to layout...')
        layout = QtGui.QGridLayout()
        layout.addWidget(cam1.get_video_display_frame(),0,0,1,1)
        layout.setContentsMargins(5,5,5,5)
        self.setLayout(layout)

configuration_page.py

from PyQt4 import QtGui, QtCore
from global_widgets import *
from ui_main import *


class ZoneConfig(QtGui.QWidget):

    def __init__(self, cam1, stacked_widget, parent=None):
        super(ZoneConfig, self).__init__(parent)
        
        GlobalObject().addEventListener("hello", self.set_cam)

        self.layout = QtGui.QVBoxLayout()
        
        self.frame = QtGui.QFrame()
        self.frame_layout = QtGui.QStackedLayout()
        # self.setLayout(self.frame_layout)

        self.screen_width = self.width()
        self.screen_height = self.height()

        cam_widget_1 = CameraDisplay(cam1, 1, self.frame_layout, stacked_widget, self)

        self.frame_layout.addWidget(cam_widget_1)

        self.frame.setLayout(self.frame_layout)
        self.layout.addWidget(self.frame)
        self.layout.setContentsMargins(0,0,0,0)
        self.setLayout(self.layout)

    @QtCore.pyqtSlot()
    def set_cam(self, index):
        if index == 1:
            self.frame_layout.setCurrentIndex(0)


class CameraDisplay(QtGui.QWidget):
    def __init__(self, cam, idx, frame_layout, stacked_widget, parent=None):
        super(CameraDisplay, self).__init__(parent)

        self.showMaximized()

        self.screen_width = self.width()
        self.screen_height = self.height()
        self.frame_layout = frame_layout
        self.stacked_widget = stacked_widget    

        print('size: ', self.screen_width, self.screen_height)

        # Layouts and frames
        layout = QtGui.QVBoxLayout()

        top_frame = QtGui.QFrame()
        top_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
        self.mid_frame = QtGui.QFrame()                                           ## Size of this frame is needed
        self.mid_frame.setStyleSheet("background-color: rgb(153, 187, 255)")
        btm_frame = QtGui.QFrame()
        btm_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
        
        # Top frame
        label = QtGui.QLabel('Configuration')
        label.setFont(QtGui.QFont('Times New Roman', 20))

        top_layout = QtGui.QHBoxLayout()
        top_layout.addWidget(label, alignment=QtCore.Qt.AlignCenter)
        top_layout.setContentsMargins(5,5,5,5)
        top_frame.setLayout(top_layout)

        # Middle frame
        mid_layout = QtGui.QHBoxLayout()

        # Create camera widgets
        print('Creating Camera Widgets...')
        mid_layout = QtGui.QHBoxLayout()
        self.video_frame = cam.get_video_frame()
        mid_layout.addWidget(self.video_frame)
        mid_layout.setContentsMargins(5,5,5,5)
        self.mid_frame.setLayout(mid_layout)

        # Bottom frame
        btn = QtGui.QPushButton('Dashboard')
        btn.clicked.connect(self.goMainWindow)

        btm_layout = QtGui.QHBoxLayout()
        btm_layout.addStretch()
        btm_layout.addWidget(btn)
        btm_layout.setContentsMargins(5,5,5,5)
        btm_frame.setLayout(btm_layout)

        layout.addWidget(top_frame, 1)
        layout.addWidget(QHLine())
        layout.addWidget(self.mid_frame, 50)
        layout.addWidget(QHLine())
        layout.addWidget(btm_frame, 1)
        layout.setContentsMargins(0,0,0,0)
        layout.setSpacing(0)

        self.setLayout(layout)
        # self.showMaximized()

        cam.set_frame_params_1(self.mid_frame.width(), self.mid_frame.height()-10)      ## here I want the size

    def resizeEvent(self, event):
        QtGui.QWidget.resizeEvent(self, event)

    def goMainWindow(self):
        self.stacked_widget.setCurrentIndex(0)

我认为问题仅出在 configuration_page.py 文件中,但我会粘贴整个代码,以防有人想了解整个逻辑。

【问题讨论】:

  • 所有小部件都有一个默认的 640x480 大小,直到它们在第一次显示时被映射(并且布局,如果使用布局管理器),这发生在调用 @987654334 之后 @。不幸的是,您的代码过于广泛,无法理解您需要这些尺寸的地点/时间/原因。在任何情况下,您只能在 显示小部件并且所有布局都完成其大小计算之后才能获得小部件的实际大小。
  • @musicamante,我需要config.py 文件中的大小,就在CameraDisplay 类中的resizeEvent() 上方。我附上了其他文件仅供参考。我应该进一步减少代码吗?
  • 如前所述,所有小部件都具有默认的 640x480 尺寸,直到它们最终第一次映射到屏幕上。由于您的问题似乎只与小部件有关(并且不需要其他所有内容,包括图像管理部分,我建议您根据现有代码创建一个minimal, reproducible example。如果您仍有问题,请编辑此问题并提供那个例子。
  • 请阅读链接并按照其说明进行操作。最小意味着它应该包含与问题相关的代码(您的代码仍然包括不必要的图像加载和管理部分),可重现意味着我们必须能够复制,粘贴并运行它(缺少某些类,例如GlobalObject)。无论如何,我已经告诉过你 ALL 小部件的大小为 640x480,直到它们最终映射到屏幕上。调用showMaximized不会立即显示和调整小部件的大小,如果你想获得最终正确的大小,你必须这样做之后
  • 您可以在resizeEvent 中执行此操作,但您不能依赖其“第一个”结果,因此如果您需要该值,您必须使用信号/槽来通知每个大小更改。请注意,如果您想知道 屏幕 大小,您当然应该获取最大化窗口的大小(特别是如果您使用返回的值,而您仍在__init__)。请改用QApplication.primaryScreen()

标签: python qt user-interface pyqt pyqt4


【解决方案1】:

将以下内容添加到您的代码中。

def event(self, e):
    if e.type() in (QEvent.Show, QEvent.Resize):
        widget_w = self.mid_frame.frameGeometry().width()
        widget_h = self.mid_frame.frameGeometry().height()
        screen_w = self.width()
        screen_h = self.height()

        # For "debugging" purposes:
        print("Widget - width: %s height: %s" % (widget_w, widget_h)
        print("screen - width: %s height: %s" % (screen_w, screen_h)
       
        return ((widget_w, widget_h), (screen_w, screen_h))

        #return {"widget": {"width": widget_w, "height": widget_h}, "screen": {"width": screen_w, "height": screen_h}}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-17
    • 2012-05-28
    • 2018-03-01
    • 2011-05-18
    • 2019-02-03
    • 1970-01-01
    • 2021-08-29
    相关资源
    最近更新 更多