Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QSplitter and resizing of QLabel with video frames does not work properly
Forum Updated to NodeBB v4.3 + New Features

QSplitter and resizing of QLabel with video frames does not work properly

Scheduled Pinned Locked Moved Unsolved General and Desktop
6 Posts 3 Posters 249 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    sapvi
    wrote on last edited by
    #1

    Hi all (again!). I have now the following situation: I want to have a viewer with several video streams (just frames visualized one after other, not actual video player). I want to have some logs under it. I want it to be resizable so that video streams & logs adapt to moving of the QSplitter. However, it seems that (I tried different pieces of code) I can never make the part with the video smaller once the video is loaded. I understand that this happens because I resize the frame to the size of the QLabel, but I do not know how I can do it some other way. Sometimes it even happens (with some workarounds I tried to apply) that QLabel and frames inside it keep increasing by themselves in some sort of feedback loop. While I approximately understand why this is happening, I am not sure what to do about it. I managed to assemble the following MRE:

    import sys
    import cv2
    import logging
    from PySide2.QtWidgets import (
        QApplication,
        QWidget,
        QVBoxLayout,
        QHBoxLayout,
        QSplitter,
        QPushButton,
        QLabel,
        QTextEdit,
        QFileDialog,
        QSizePolicy,
    )
    from PySide2.QtCore import Qt, QTimer
    from PySide2.QtGui import QImage, QPixmap
    
    
    class VideoViewer(QWidget):
        def __init__(self):
            super().__init__()
            self.cap = None
            self.paused = True
    
            # Layout for the video viewer
            layout = QVBoxLayout(self)
    
            # Video display label
            self.video_label = QLabel("Video stream will be displayed here")
            self.video_label.setAlignment(Qt.AlignCenter)
            self.video_label.setStyleSheet("background-color: black; color: white")
            self.video_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            layout.addWidget(self.video_label)
    
            # Control buttons
            controls_layout = QHBoxLayout()
            self.load_button = QPushButton("Load Video")
            self.load_button.clicked.connect(self.load_video)
            controls_layout.addWidget(self.load_button)
    
            self.play_pause_button = QPushButton("Play")
            self.play_pause_button.clicked.connect(self.play_pause_video)
            controls_layout.addWidget(self.play_pause_button)
    
            layout.addLayout(controls_layout)
    
            # Timer for video frame updates
            self.timer = QTimer()
            self.timer.timeout.connect(self.update_frame)
    
        def load_video(self):
            video_path, _ = QFileDialog.getOpenFileName(self, "Select Video File")
            if video_path:
                self.cap = cv2.VideoCapture(video_path)
                self.paused = True
                self.play_pause_button.setText("Play")
                logging.info(f"Loaded video: {video_path}")
    
        def play_pause_video(self):
            if self.cap is None:
                return
            if self.paused:
                self.paused = False
                self.play_pause_button.setText("Pause")
                self.timer.start(30)  # Update every 30 ms
                logging.info("Video playback started.")
            else:
                self.paused = True
                self.play_pause_button.setText("Play")
                self.timer.stop()
                logging.info("Video playback paused.")
    
        def update_frame(self):
            if self.cap is not None and not self.paused:
                ret, frame = self.cap.read()
                if ret:
                    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                    label_size = self.video_label.size()
                    frame = cv2.resize(frame, (label_size.width(), label_size.height()))
                    height, width, _ = frame.shape
                    q_image = QImage(frame.data, width, height, QImage.Format_RGB888)
                    self.video_label.setPixmap(QPixmap.fromImage(q_image))
                else:
                    self.timer.stop()
                    self.cap.release()
                    self.cap = None
                    self.play_pause_button.setText("Play")
                    logging.info("Video playback finished.")
    
    
    class LogHandler(logging.Handler):
        def __init__(self, text_edit):
            super().__init__()
            self.text_edit = text_edit
    
        def emit(self, record):
            msg = self.format(record)
            self.text_edit.append(msg)
    
    
    class MainUI(QWidget):
        def __init__(self):
            super().__init__()
    
            # Main layout
            main_layout = QVBoxLayout(self)
    
            # Splitter to divide video viewer and log panel
            self.splitter = QSplitter(Qt.Vertical)
            main_layout.addWidget(self.splitter)
    
            # Video viewer panel
            self.viewer_panel = VideoViewer()
            self.splitter.addWidget(self.viewer_panel)
    
            # Log panel
            self.log_text_edit = QTextEdit()
            self.log_text_edit.setReadOnly(True)
            self.log_text_edit.setStyleSheet("background-color: black; color: white;")
            self.splitter.addWidget(self.log_text_edit)
    
            # Set initial splitter sizes
            self.splitter.setSizes([800, 200])
    
            # Configure logging
            log_handler = LogHandler(self.log_text_edit)
            log_handler.setFormatter(
                logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
            )
            logging.getLogger().addHandler(log_handler)
            logging.getLogger().setLevel(logging.INFO)
            logging.info("Application started.")
    
            self.setLayout(main_layout)
            self.setWindowTitle("Video Player with Log Panel")
            self.resize(800, 600)
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = MainUI()
        window.show()
        sys.exit(app.exec_())
    

    What should I change about this code, so I can freely move QSplitter in the way that video can also get smaller, not only bigger, and that of course no feedback loop with random video growing happens? My goal is to have multiple videos but I think the same strategy as for one video in this MRE would work. I expect video to also adapt automatically if the user resizes window. It used to work till I introduced QSplitter, not it freezes and/or crashes in the MRE I provided above.
    Thanks in advance!

    Pl45m4P 1 Reply Last reply
    0
    • S sapvi

      Hi all (again!). I have now the following situation: I want to have a viewer with several video streams (just frames visualized one after other, not actual video player). I want to have some logs under it. I want it to be resizable so that video streams & logs adapt to moving of the QSplitter. However, it seems that (I tried different pieces of code) I can never make the part with the video smaller once the video is loaded. I understand that this happens because I resize the frame to the size of the QLabel, but I do not know how I can do it some other way. Sometimes it even happens (with some workarounds I tried to apply) that QLabel and frames inside it keep increasing by themselves in some sort of feedback loop. While I approximately understand why this is happening, I am not sure what to do about it. I managed to assemble the following MRE:

      import sys
      import cv2
      import logging
      from PySide2.QtWidgets import (
          QApplication,
          QWidget,
          QVBoxLayout,
          QHBoxLayout,
          QSplitter,
          QPushButton,
          QLabel,
          QTextEdit,
          QFileDialog,
          QSizePolicy,
      )
      from PySide2.QtCore import Qt, QTimer
      from PySide2.QtGui import QImage, QPixmap
      
      
      class VideoViewer(QWidget):
          def __init__(self):
              super().__init__()
              self.cap = None
              self.paused = True
      
              # Layout for the video viewer
              layout = QVBoxLayout(self)
      
              # Video display label
              self.video_label = QLabel("Video stream will be displayed here")
              self.video_label.setAlignment(Qt.AlignCenter)
              self.video_label.setStyleSheet("background-color: black; color: white")
              self.video_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
              layout.addWidget(self.video_label)
      
              # Control buttons
              controls_layout = QHBoxLayout()
              self.load_button = QPushButton("Load Video")
              self.load_button.clicked.connect(self.load_video)
              controls_layout.addWidget(self.load_button)
      
              self.play_pause_button = QPushButton("Play")
              self.play_pause_button.clicked.connect(self.play_pause_video)
              controls_layout.addWidget(self.play_pause_button)
      
              layout.addLayout(controls_layout)
      
              # Timer for video frame updates
              self.timer = QTimer()
              self.timer.timeout.connect(self.update_frame)
      
          def load_video(self):
              video_path, _ = QFileDialog.getOpenFileName(self, "Select Video File")
              if video_path:
                  self.cap = cv2.VideoCapture(video_path)
                  self.paused = True
                  self.play_pause_button.setText("Play")
                  logging.info(f"Loaded video: {video_path}")
      
          def play_pause_video(self):
              if self.cap is None:
                  return
              if self.paused:
                  self.paused = False
                  self.play_pause_button.setText("Pause")
                  self.timer.start(30)  # Update every 30 ms
                  logging.info("Video playback started.")
              else:
                  self.paused = True
                  self.play_pause_button.setText("Play")
                  self.timer.stop()
                  logging.info("Video playback paused.")
      
          def update_frame(self):
              if self.cap is not None and not self.paused:
                  ret, frame = self.cap.read()
                  if ret:
                      frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                      label_size = self.video_label.size()
                      frame = cv2.resize(frame, (label_size.width(), label_size.height()))
                      height, width, _ = frame.shape
                      q_image = QImage(frame.data, width, height, QImage.Format_RGB888)
                      self.video_label.setPixmap(QPixmap.fromImage(q_image))
                  else:
                      self.timer.stop()
                      self.cap.release()
                      self.cap = None
                      self.play_pause_button.setText("Play")
                      logging.info("Video playback finished.")
      
      
      class LogHandler(logging.Handler):
          def __init__(self, text_edit):
              super().__init__()
              self.text_edit = text_edit
      
          def emit(self, record):
              msg = self.format(record)
              self.text_edit.append(msg)
      
      
      class MainUI(QWidget):
          def __init__(self):
              super().__init__()
      
              # Main layout
              main_layout = QVBoxLayout(self)
      
              # Splitter to divide video viewer and log panel
              self.splitter = QSplitter(Qt.Vertical)
              main_layout.addWidget(self.splitter)
      
              # Video viewer panel
              self.viewer_panel = VideoViewer()
              self.splitter.addWidget(self.viewer_panel)
      
              # Log panel
              self.log_text_edit = QTextEdit()
              self.log_text_edit.setReadOnly(True)
              self.log_text_edit.setStyleSheet("background-color: black; color: white;")
              self.splitter.addWidget(self.log_text_edit)
      
              # Set initial splitter sizes
              self.splitter.setSizes([800, 200])
      
              # Configure logging
              log_handler = LogHandler(self.log_text_edit)
              log_handler.setFormatter(
                  logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
              )
              logging.getLogger().addHandler(log_handler)
              logging.getLogger().setLevel(logging.INFO)
              logging.info("Application started.")
      
              self.setLayout(main_layout)
              self.setWindowTitle("Video Player with Log Panel")
              self.resize(800, 600)
      
      
      if __name__ == "__main__":
          app = QApplication(sys.argv)
          window = MainUI()
          window.show()
          sys.exit(app.exec_())
      

      What should I change about this code, so I can freely move QSplitter in the way that video can also get smaller, not only bigger, and that of course no feedback loop with random video growing happens? My goal is to have multiple videos but I think the same strategy as for one video in this MRE would work. I expect video to also adapt automatically if the user resizes window. It used to work till I introduced QSplitter, not it freezes and/or crashes in the MRE I provided above.
      Thanks in advance!

      Pl45m4P Offline
      Pl45m4P Offline
      Pl45m4
      wrote on last edited by
      #2

      @sapvi said in QSplitter and resizing of QLabel with video frames does not work properly:

      I want to have a viewer with several video streams (just frames visualized one after other, not actual video player)

      What do you think a video is and what a video player does? :)
      Videos are just consequencial frames (with or without some additional encoding)

      @sapvi said in QSplitter and resizing of QLabel with video frames does not work properly:

      What should I change about this code, so I can freely move QSplitter in the way that video can also get smaller, not only bigger, and that of course no feedback loop with random video growing happens?

      A splitter is expanding by default. This in combination with the other widget's size policies lets everything populate more space but does not automatically shrink again.
      Modify your widget's and layout's sizeHints and policies.


      If debugging is the process of removing software bugs, then programming must be the process of putting them in.

      ~E. W. Dijkstra

      S 1 Reply Last reply
      1
      • Pl45m4P Pl45m4

        @sapvi said in QSplitter and resizing of QLabel with video frames does not work properly:

        I want to have a viewer with several video streams (just frames visualized one after other, not actual video player)

        What do you think a video is and what a video player does? :)
        Videos are just consequencial frames (with or without some additional encoding)

        @sapvi said in QSplitter and resizing of QLabel with video frames does not work properly:

        What should I change about this code, so I can freely move QSplitter in the way that video can also get smaller, not only bigger, and that of course no feedback loop with random video growing happens?

        A splitter is expanding by default. This in combination with the other widget's size policies lets everything populate more space but does not automatically shrink again.
        Modify your widget's and layout's sizeHints and policies.

        S Offline
        S Offline
        sapvi
        wrote on last edited by
        #3

        @Pl45m4 sorry, I am new to Qt and not sure in which direction to explore with this advice.

        I have tried using self.video_label.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) instead of Expanding policy. Then:

        1. In MRE, I can resize several times, but artefacts are happening during the resize (loss of color etc), and after ~3rd resize all app just crashes.
        2. If I try it in my actual bigger app, I do not even see videos, they get automatically minimal size of (0,0) in 2x3 grid which I use. And I want them to automatically expand to the currently-available size within the corresponding section of the QSplitter. Just to not block QSplitter from reducing this section :(
        Pl45m4P 1 Reply Last reply
        0
        • S sapvi

          @Pl45m4 sorry, I am new to Qt and not sure in which direction to explore with this advice.

          I have tried using self.video_label.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) instead of Expanding policy. Then:

          1. In MRE, I can resize several times, but artefacts are happening during the resize (loss of color etc), and after ~3rd resize all app just crashes.
          2. If I try it in my actual bigger app, I do not even see videos, they get automatically minimal size of (0,0) in 2x3 grid which I use. And I want them to automatically expand to the currently-available size within the corresponding section of the QSplitter. Just to not block QSplitter from reducing this section :(
          Pl45m4P Offline
          Pl45m4P Offline
          Pl45m4
          wrote on last edited by
          #4

          @sapvi

          Haven't run your example because I don't use OpenCV for Python (you can't expect everyone to have that installed to run your example). But for Python it's the same as for C++ in most of the cases.
          Also when you experience continuous growth of your label/layout it might be related to the fact that you scale your image to your label size... the content shouldn't be exact the same size as the "frame" (QLabel is a QFrame) around it as it might resize itself then to contain the image properly.


          If debugging is the process of removing software bugs, then programming must be the process of putting them in.

          ~E. W. Dijkstra

          S 1 Reply Last reply
          0
          • Pl45m4P Pl45m4

            @sapvi

            Haven't run your example because I don't use OpenCV for Python (you can't expect everyone to have that installed to run your example). But for Python it's the same as for C++ in most of the cases.
            Also when you experience continuous growth of your label/layout it might be related to the fact that you scale your image to your label size... the content shouldn't be exact the same size as the "frame" (QLabel is a QFrame) around it as it might resize itself then to contain the image properly.

            S Offline
            S Offline
            sapvi
            wrote on last edited by
            #5

            @Pl45m4 good point, did not think about it. I changed example to generate random QImage. Issues are same: namely problems with resizing & crashing. In my actual app I do not get crash, surprisingly, but same resize issues.

            import sys
            import logging
            import numpy as np
            
            from PySide2.QtWidgets import (
                QApplication,
                QWidget,
                QVBoxLayout,
                QHBoxLayout,
                QSplitter,
                QPushButton,
                QLabel,
                QTextEdit,
                QSizePolicy,
            )
            from PySide2.QtCore import Qt, QTimer
            from PySide2.QtGui import QImage, QPixmap
            
            
            class VideoViewer(QWidget):
                def __init__(self):
                    super().__init__()
            
                    # Layout for the video viewer
                    layout = QVBoxLayout(self)
            
                    # Create QLabel as Fake Video Display
                    self.video_label = QLabel()
                    self.video_label.setAlignment(Qt.AlignCenter)
                    self.video_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
                    self.video_label.setStyleSheet("background-color: black;")
                    layout.addWidget(self.video_label)
            
                    # Control buttons
                    controls_layout = QHBoxLayout()
                    self.play_pause_button = QPushButton("Play Fake Video")
                    self.play_pause_button.clicked.connect(self.toggle_fake_video)
                    controls_layout.addWidget(self.play_pause_button)
            
                    layout.addLayout(controls_layout)
                    self.setLayout(layout)
            
                    # Timer for updating the fake video
                    self.timer = QTimer()
                    self.timer.timeout.connect(self.update_fake_video)
                    self.running = False
            
                def toggle_fake_video(self):
                    """Start or stop fake video playback"""
                    if self.running:
                        self.timer.stop()
                        self.play_pause_button.setText("Play Fake Video")
                        logging.info("Fake video stopped.")
                    else:
                        self.timer.start(30)  # Update every 30 ms
                        self.play_pause_button.setText("Stop Fake Video")
                        logging.info("Fake video started.")
                    self.running = not self.running
            
                def update_fake_video(self):
                    """Generate a random-colored QImage and display it in QLabel"""
                    width = self.video_label.width()
                    height = self.video_label.height()
            
                    if width <= 0 or height <= 0:
                        return  # Avoid errors when widget is hidden or uninitialized
            
                    # Generate random pixel values (3 channels for RGB)
                    random_image = np.random.randint(0, 256, (height, width, 3), dtype=np.uint8)
            
                    # Create QImage from numpy array
                    q_image = QImage(random_image.data, width, height, QImage.Format_RGB888)
            
                    # Convert QImage to QPixmap and display it
                    self.video_label.setPixmap(QPixmap.fromImage(q_image))
            
            
            class LogHandler(logging.Handler):
                def __init__(self, text_edit):
                    super().__init__()
                    self.text_edit = text_edit
            
                def emit(self, record):
                    msg = self.format(record)
                    self.text_edit.append(msg)
            
            
            class MainUI(QWidget):
                def __init__(self):
                    super().__init__()
            
                    # Main layout
                    main_layout = QVBoxLayout(self)
            
                    # Splitter to divide video viewer and log panel
                    self.splitter = QSplitter(Qt.Vertical)
                    main_layout.addWidget(self.splitter)
            
                    # Video viewer panel
                    self.viewer_panel = VideoViewer()
                    self.splitter.addWidget(self.viewer_panel)
                    self.viewer_panel.setMinimumSize(0, 0)
            
                    # Log panel
                    self.log_text_edit = QTextEdit()
                    self.log_text_edit.setReadOnly(True)
                    self.log_text_edit.setStyleSheet("background-color: black; color: white;")
                    self.log_text_edit.setMinimumSize(0, 0)
            
                    self.splitter.addWidget(self.log_text_edit)
            
                    # Allow collapsing of both sections
                    self.splitter.setCollapsible(0, True)
                    self.splitter.setCollapsible(1, True)
            
                    # Set initial splitter sizes
                    self.splitter.setSizes([800, 200])
            
                    # Configure logging
                    log_handler = LogHandler(self.log_text_edit)
                    log_handler.setFormatter(
                        logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
                    )
                    logging.getLogger().addHandler(log_handler)
                    logging.getLogger().setLevel(logging.INFO)
                    logging.info("Application started.")
            
                    self.setLayout(main_layout)
                    self.setWindowTitle("Fake Video Player with Log Panel")
                    self.resize(800, 600)
            
            
            if __name__ == "__main__":
                app = QApplication(sys.argv)
                window = MainUI()
                window.show()
                sys.exit(app.exec_())
            
            1 Reply Last reply
            0
            • Z Offline
              Z Offline
              Zbigniew-Sch
              wrote on last edited by
              #6

              Hi,

              Use "QScrollArea" in your "QSplitter" and not "QLabel", then create "QVideoWidget" and use the object as follows:

              QSplitter *pcoYourSplitter = new QSplitter(Qt::Orientation::Horizontal);
              pcoYourLayout->addWidget(pcoYourSplitter);

              QScrollArea *pcoYourScrollArea = new QScrollArea();
              QVideoWidget *pcoYourVideoWidget = new QVideoWidget(pcoYourScrollArea);

              // Add widget or replace widgt - replaceWidget(0, pcoYourVideoWidget)
              pcoYourSplitter->addWidget(pcoYourVideoWidget);

              and then start your video.
              For me, this works fine in C++ and Python (also in C++ MDI area).

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved