1

I need some of my widgets to resize with the window as the user makes it larger or smaller, and I've already looked at this question, but unfortunately all the answers there solve the problem by assigning a layout to the main window. Doing so is not an option for me, as I've found it impossible to lay out the widgets the way I want with any of the layout modes offered by Qt Designer; in other words, the layout being set as "broken" is by design, in my case.

I have only three widgets I wish to make responsive: the two boxes you can see in the screenshot below and the big button on the top side. Everything else should stay where it is. How can I achieve this?

screenshot of the app

To be exact, as the window grows, I would like the two boxes to grow vertically and horizontally in equal measure, while the button should only grow horizontally. I don't need this to be done in the ui file necessarily: code solutions are ok as well.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
theberzi
  • 2,142
  • 3
  • 20
  • 34

1 Answers1

3

The basic Qt layouts (grid and boxed) are very simple as much as they are powerful, and there are very rare and specific cases for which manual override of the geometry is actually required; even in those cases, one should be very careful with it, as deep knowledge and understanding of Qt size management is required in order to avoid unexpected behavior that could result in an unusable UI or even recursion issues.

99% of the times it's enough to be aware of how layout managers work, and take some time in realizing how the whole layout should be structured, possibly by using nested layouts with proper adjustments of stretches and size policies. Hint: drawing a sketch on paper helps a lot.

In your case, a possible solution could use the following structure:

  • a main vertical layout for the central widget
    • a grid layout for the top elements, with:
      • the label and the line edit on the first row, one cell each
      • a spacer
      • the button that occupies two rows of the grid
    • a vertical layout for each of the two groups, with:
      • the label
      • the scroll area
      • a horizontal layout for the bottom elements, with:
        • the button
        • another spacer
        • the checkbox

Then, the line edit has a Preferred horizontal size policy, the "Scan now" button has a minimum height and an Expanding horizontal policy instead; the two bottom buttons have the same minimum size, the checkboxes have an Expanding horizontal policy; the two scroll areas are left as they are, as they expand themselves by default (and since they're the only vertically expanding widgets, they will expand equally). All spacers have a fixed width (by default they are "expanding" spacers).

Note that, while I could have used a grid layout for the two bottom "groups", using a nested vertical/horizontal layout is often more flexible; in any case, another possible variation could have been to use a separate grid layout for both those two groups (since they have common element, sizes and behaviors), with both the "header" labels and scroll areas spanning 3 columns.

Here you can see the structure as it will appear in Designer:

layout screenshot

And here is the 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>454</width>
    <height>405</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout_3">
    <item>
     <layout class="QGridLayout" name="gridLayout">
      <item row="0" column="0">
       <widget class="QLabel" name="label">
        <property name="text">
         <string>Scan:</string>
        </property>
       </widget>
      </item>
      <item row="0" column="1">
       <widget class="QLineEdit" name="lineEdit">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
       </widget>
      </item>
      <item row="0" column="2">
       <spacer name="gridSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeType">
         <enum>QSizePolicy::Fixed</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item row="0" column="3" rowspan="2">
       <widget class="QPushButton" name="pushButton">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
        <property name="minimumSize">
         <size>
          <width>0</width>
          <height>50</height>
         </size>
        </property>
        <property name="text">
         <string>Scan now</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
    <item>
     <layout class="QVBoxLayout" name="detectVertLayout">
      <item>
       <widget class="QLabel" name="detectLabel">
        <property name="text">
         <string>Detected:</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QTextEdit" name="detectTextEdit"/>
      </item>
      <item>
       <layout class="QHBoxLayout" name="detectBtnLayout">
        <item>
         <widget class="QPushButton" name="pasteButton">
          <property name="minimumSize">
           <size>
            <width>100</width>
            <height>0</height>
           </size>
          </property>
          <property name="icon">
           <iconset theme="edit-paste"/>
          </property>
         </widget>
        </item>
        <item>
         <spacer name="horizontalSpacer">
          <property name="orientation">
           <enum>Qt::Horizontal</enum>
          </property>
          <property name="sizeType">
           <enum>QSizePolicy::Fixed</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>40</width>
            <height>20</height>
           </size>
          </property>
         </spacer>
        </item>
        <item>
         <widget class="QCheckBox" name="copyCheck">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
          <property name="text">
           <string>Copy automatically</string>
          </property>
         </widget>
        </item>
       </layout>
      </item>
     </layout>
    </item>
    <item>
     <layout class="QVBoxLayout" name="translVertLayout">
      <item>
       <widget class="QLabel" name="translateLabel">
        <property name="text">
         <string>Translations:</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QTextEdit" name="translateTextEdit"/>
      </item>
      <item>
       <layout class="QHBoxLayout" name="translBtnLayout">
        <item>
         <widget class="QPushButton" name="translButton">
          <property name="minimumSize">
           <size>
            <width>100</width>
            <height>0</height>
           </size>
          </property>
          <property name="text">
           <string>Translate</string>
          </property>
         </widget>
        </item>
        <item>
         <spacer name="horizontalSpacer_2">
          <property name="orientation">
           <enum>Qt::Horizontal</enum>
          </property>
          <property name="sizeType">
           <enum>QSizePolicy::Fixed</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>40</width>
            <height>20</height>
           </size>
          </property>
         </spacer>
        </item>
        <item>
         <widget class="QCheckBox" name="autoTranslCheck">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
          <property name="text">
           <string>Translate automatically</string>
          </property>
         </widget>
        </item>
       </layout>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>454</width>
     <height>24</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

I suggest you to load the above file in Designer and patiently study it; most importantly, take a closer look at overridden properties of each element: row/column spanning, minimum/maximum sizes, size policies and stretches.
Then do some reading about:

Finally, do lots and lots of tests and experiments, both in Designer and by code.

musicamante
  • 41,230
  • 6
  • 33
  • 58
  • Thank you. I ended up recreating it manually, it was easier than expected. Making the Scan now button span two rows was a bit tricky: I had to insert a spacer to create a second row, drag the button to span both, then delete the spacer. Probably there is a better way to do it? I also had to initially break the layout so that I could place stuff inside the sub-layouts: if I place a layout inside a vertical layout it gets flattened to a line and I couldn't find a way to place widgets inside it. – theberzi May 17 '21 at 08:03
  • 1
    @theberzi: that's the correct way to set a span without other widgets in the "spanned" row or column, just use a placeholder for the required cells, add the spanning widget and remove the placeholder. The trick for the last problem is to change one of the contents margins of the layout in the property inspector, depending on the orientation: in the case above, set a top or bottom margin bigger than 0, add the widgets you need, and then *reset* the margin (click the small "undo" arrow, do not set it manually back to 0). – musicamante May 17 '21 at 15:09