Skip to content
Fix Code Error

Layout doesn’t expand more than initial size it was set using designer

June 20, 2021 by Code Error
Posted By: Anonymous

I’m trying to resize my widgets proportional to GUI window size but got stuck.

First, I put some widgets, including QVBoxLayout, using designer and load it using PyQt5.uic.loadUi().

After that I tried to resize some of the widgets via resizeEvent:

def resizeEvent(self, event):
    print("Resized")
    QtWidgets.QMainWindow.resizeEvent(self, event)
    self.resize_widget(self.label, 'both', self.ratio_label)
    self.resize_widget(self.slider_cv2, 'width', self.ratio_slider_cv2)
    self.resize_widget(self.graph_layout, 'both', self.ratio_graph_layout)

def resize_widget(self, widget, mode, ratio):
    if mode == 'width':
        widget.resize(int(self.size().width() * ratio), widget.geometry().height())
    elif mode == 'height':
        widget.resize(widget.geometry().width(), int(self.size().height() * ratio))
    else:
        try:
            widget.resize(int(self.size().width() * ratio[0]), int(self.size().height() * ratio[1]))
        except AttributeError:
            widget.setGeometry(QtCore.QRect(widget.geometry().left(), widget.geometry().top(), int(self.geometry().width() * ratio[0]), int(self.geometry().height() * ratio[1])))

self.label, self.slider_cv2, and self.graph_layout are instances of Qlabel, QSlider, and QVBoxLayout, respectively.

Sizes of self.label and self.slider_cv2 change dynamically as I expected, but self.graph_layout doesn’t. It gets smaller when the GUI window gets smaller, yet does not grow bigger than its initial size.


Added minimal reproducible code:

from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QVideoFrame
from PyQt5.QtCore import QUrl, QTimer
from PyQt5.QtGui import QPixmap, QImage
import sys
import os
import threading
app = QtWidgets.QApplication(sys.argv)
import cv2
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from time import sleep
import numpy as np


class UI(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        uic.loadUi('/workspace/demo_cv2_load_entire_layout.ui', self)

        self.fig = plt.Figure()
        self.canvas = FigureCanvas(self.fig)
        self.graph_layout.addWidget(self.canvas)

        #---------For resizeing widget-----------
        w, h = self.size().width(), self.size().height()
        self.graph_layout.activate()
        self.ratio_graph_layout = (self.graph_layout.geometry().width() / w, self.graph_layout.geometry().height() / h)
        self.ratio_label = (self.label.size().width() / w, self.label.size().height() / h)
        self.ratio_slider_cv2 = self.slider_cv2.size().width() / w
        self.initial_wh = (w, h)
        #----------------------------------------
        self.show()
        
    def resizeEvent(self, event):
        print("Resized")
        QtWidgets.QMainWindow.resizeEvent(self, event)
        self.resize_widget(self.label, 'both', self.ratio_label)
        self.resize_widget(self.slider_cv2, 'width', self.ratio_slider_cv2)
        self.resize_widget(self.graph_layout, 'both', self.ratio_graph_layout)
    
    def resize_widget(self, widget, mode, ratio):
        if mode == 'width':
            widget.resize(int(self.size().width() * ratio), widget.geometry().height())
        elif mode == 'height':
            widget.resize(widget.geometry().width(), int(self.size().height() * ratio))
        else:
            try:
                widget.resize(int(self.size().width() * ratio[0]), int(self.size().height() * ratio[1]))
            except AttributeError:
                widget.setGeometry(QtCore.QRect(widget.geometry().left(), widget.geometry().top(), int(self.geometry().width() * ratio[0]), int(self.geometry().height() * ratio[1])))
        print(widget.geometry())


if __name__ == "__main__":
    window = UI()
    app.exec_()

And .UI file:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>1018</width>
    <height>814</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QWidget" name="verticalLayoutWidget">
    <property name="geometry">
     <rect>
      <x>240</x>
      <y>410</y>
      <width>291</width>
      <height>151</height>
     </rect>
    </property>
    <layout class="QVBoxLayout" name="graph_layout">
     <property name="sizeConstraint">
      <enum>QLayout::SetMaximumSize</enum>
     </property>
    </layout>
   </widget>
   <widget class="QSlider" name="slider_cv2">
    <property name="geometry">
     <rect>
      <x>220</x>
      <y>620</y>
      <width>349</width>
      <height>15</height>
     </rect>
    </property>
    <property name="orientation">
     <enum>Qt::Horizontal</enum>
    </property>
   </widget>
   <widget class="QLabel" name="label">
    <property name="geometry">
     <rect>
      <x>90</x>
      <y>0</y>
      <width>711</width>
      <height>371</height>
     </rect>
    </property>
    <property name="frameShape">
     <enum>QFrame::Box</enum>
    </property>
    <property name="text">
     <string>Video</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>1018</width>
     <height>20</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

Solution

If you want to resize an element manually then you shouldn’t use a layout. In this you must use a QWidget as a container and then place the FigureCanvas in that container using a layout.

On the other hand, I have implemented a logic that simplifies and allows easy handling of the resize functionality without the need for resizeEvent override.

*.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>1018</width>
    <height>814</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QSlider" name="slider_cv2">
    <property name="geometry">
     <rect>
      <x>220</x>
      <y>620</y>
      <width>349</width>
      <height>15</height>
     </rect>
    </property>
    <property name="orientation">
     <enum>Qt::Horizontal</enum>
    </property>
   </widget>
   <widget class="QLabel" name="label">
    <property name="geometry">
     <rect>
      <x>90</x>
      <y>0</y>
      <width>711</width>
      <height>371</height>
     </rect>
    </property>
    <property name="frameShape">
     <enum>QFrame::Box</enum>
    </property>
    <property name="text">
     <string>Video</string>
    </property>
   </widget>
   <widget class="QWidget" name="graph_container" native="true">
    <property name="geometry">
     <rect>
      <x>240</x>
      <y>390</y>
      <width>291</width>
      <height>151</height>
     </rect>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>1018</width>
     <height>28</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

*.py

import os
import sys
from dataclasses import dataclass
from enum import IntFlag, auto
from pathlib import Path


from PyQt5 import QtWidgets, uic, QtCore

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas


class ModeRatio(IntFlag):
    NONE = auto()
    WIDTH = auto()
    HEIGHT = auto()
    BOTH = WIDTH | HEIGHT


@dataclass
class SizeManager(QtCore.QObject):
    widget: QtWidgets.QWidget
    mode: ModeRatio

    def __post_init__(self):
        super().__init__(self.widget)
        self.widget.window().installEventFilter(self)
        self._width_ratio = self.widget.width() * 1.0 / self.widget.window().width()
        self._height_ratio = self.widget.height() * 1.0 / self.widget.window().height()

    def eventFilter(self, obj, event):
        if obj is self.widget.window():
            if event.type() == QtCore.QEvent.Resize:
                self.apply_resize()
        return super().eventFilter(obj, event)

    def apply_resize(self):
        width = self.widget.width()
        height = self.widget.height()
        if self.mode & ModeRatio.WIDTH:
            width = self.widget.window().width() * self._width_ratio
        if self.mode & ModeRatio.HEIGHT:
            height = self.widget.window().height() * self._height_ratio
        self.widget.resize(QtCore.QSizeF(width, height).toSize())


class UI(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        filename = os.fspath(
            Path(__file__).resolve().parent / "demo_cv2_load_entire_layout.ui"
        )
        uic.loadUi(filename, self)

        self.fig = Figure()
        self.canvas = FigureCanvas(self.fig)

        lay = QtWidgets.QVBoxLayout(self.graph_container)
        lay.addWidget(self.canvas)

        self.label_size_manager = SizeManager(widget=self.label, mode=ModeRatio.BOTH)
        self.slider_size_manager = SizeManager(
            widget=self.slider_cv2, mode=ModeRatio.WIDTH
        )
        self.graph_size_manager = SizeManager(
            widget=self.graph_container, mode=ModeRatio.BOTH
        )


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)

    window = UI()
    window.show()

    app.exec_()
├── demo_cv2_load_entire_layout.ui
└── main.py
Answered By: Anonymous

Related Articles

  • Error 'Map', but got one of type 'Null' flutter web with…
  • How to dynamically add widgets to a layout after a button is…
  • simulate mouse drag event PyQt5
  • Python is not calling fucntions properly
  • Multiple context menu in a single qTableView pyqt5
  • Combining pyOSC with pyQT5 / Threading?
  • drawControl not receiving correct QStyleOption
  • How to store the last checked item as default, when Opening…
  • How do I load a ui in pyqt using a string
  • Donut piechart in mplcanvas

Disclaimer: This content is shared under creative common license cc-by-sa 3.0. It is generated from StackExchange Website Network.

Post navigation

Previous Post:

Apply attribute-sets on existing elements

Next Post:

Table row edit function is not working in angular

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

  • Get code errors & solutions at akashmittal.com
© 2022 Fix Code Error