0

I know there are some answers provided regarding the QTabWidget resize (Link 1, Link 2, and others); however, I can't get it working properly in my application.

The idea is to create a window that resizes according to the QTabWidget size. Currently, I have two tabs, and the first one has a smaller size compared with the other one.

When I press the second tab, the window size increases as intended but, if I go back to the first tab, the window doesn't shrink. I tried changing the QSizePolicy of each tab as well as resizing the tab and the window but it doesn't change the outcome.

How can I solve the fact the window doesn't shrink? Here is the code:


import os
import os.path
import sys

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
    QSizePolicy,
    QApplication,
    QLabel,
    QMainWindow,
    QTabWidget,
    QDesktopWidget,
    QRadioButton,
    QLineEdit,
    QVBoxLayout,
    QHBoxLayout,
    QGridLayout,
    QWidget,
)

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupMainUi()
    
    def setupMainUi(self):
        
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)
        main_window_layout = QVBoxLayout(self.centralWidget)
        self.centralWidget.setLayout(main_window_layout)

        self.tabs = QTabWidget()
        self.tabs.addTab(self.loadTab1(), "Tab1")
        self.tabs.addTab(self.loadTab2(), "Tab2")

        self.tabs.currentChanged.connect(self.updateSizes)
        self.updateSizes(0)
        
        main_window_layout.addWidget(self.tabs)

    def updateSizes(self, index):
            
        for i in range(self.tabs.count()):
            if i != index:
                self.tabs.widget(i).setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        
        self.tabs.widget(index).setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.tabs.widget(index).resize(self.tabs.widget(index).minimumSizeHint())
        self.tabs.widget(index).adjustSize()
        self.resize(self.minimumSize())
        self.adjustSize()
   
    def loadTab1(self):
        
        self.ticketTab = QWidget()
        self.ticketTab.setEnabled(False)

        date_layout = QGridLayout()
        self.ticket = QLabel("Number:", self)
        self.ticket_number = QLineEdit()
        self.ticket_number.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        date_layout.addWidget(self.ticket, 0, 0)
        date_layout.addWidget(self.ticket_number, 0, 1)
        
        self.ticketTab.setLayout(date_layout)
        
        return self.ticketTab 
    
    def loadTab2(self):
        
        self.familyTab = QWidget()
        self.familyTab.setEnabled(False)
        
        family_default_layout = QVBoxLayout()
        self.family_default_checkbox = QRadioButton("Default")
        self.family_default_checkbox.setChecked(True)
        self.family_default_checkbox.setLayoutDirection(Qt.RightToLeft) 
        family_default_layout.addWidget(self.family_default_checkbox, alignment = Qt.AlignRight)
        
        family_layout = QHBoxLayout() 
        self.family_label = QLabel("Number of Families:", self)
        self.family_number = QLineEdit()
        self.family_number.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.family_number.textChanged.connect(lambda:self.checkInput(self.family_number))
        family_layout.addWidget(self.family_label, alignment=Qt.AlignLeft | Qt.AlignVCenter)
        family_layout.addWidget(self.family_number, alignment=Qt.AlignLeft | Qt.AlignVCenter)
        family_default_layout.addLayout(family_layout)
        
        temp_family_layout = QHBoxLayout() 
        self.family_label_1 = QLabel("Number of Families:", self)
        self.family_number_1 = QLineEdit()
        self.family_number_1.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        temp_family_layout.addWidget(self.family_label_1, alignment=Qt.AlignLeft | Qt.AlignVCenter)
        temp_family_layout.addWidget(self.family_number_1, alignment=Qt.AlignLeft | Qt.AlignVCenter)
        family_default_layout.addLayout(temp_family_layout)
        
        min_sub_family_layout = QHBoxLayout() 
        self.min_subfamily_label = QLabel("Minimum Number of SubFamilies:", self)
        self.min_subfamily_number = QLineEdit()
        self.min_subfamily_number.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        min_sub_family_layout.addWidget(self.min_subfamily_label, alignment=Qt.AlignLeft | Qt.AlignVCenter)
        min_sub_family_layout.addWidget(self.min_subfamily_number, alignment=Qt.AlignLeft | Qt.AlignVCenter)
        family_default_layout.addLayout(min_sub_family_layout)
        
        max_sub_family_layout = QHBoxLayout() 
        self.max_subfamily_label = QLabel("Maximum Number of SubFamilies:", self)
        self.max_subfamily_number = QLineEdit()
        self.max_subfamily_number.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        max_sub_family_layout.addWidget(self.max_subfamily_label, alignment=Qt.AlignLeft | Qt.AlignVCenter)
        max_sub_family_layout.addWidget(self.max_subfamily_number, alignment=Qt.AlignLeft | Qt.AlignVCenter)
        family_default_layout.addLayout(max_sub_family_layout)
        
        self.familyTab.setLayout(family_default_layout)
        
        return self.familyTab
    
os.chdir("../..")

app = QApplication(sys.argv)
win = MainWindow()

qr = win.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
win.move(qr.topLeft()) 
win.move(win.pos().x(), win.pos().y() - 180)

win.show()
sys.exit(app.exec())

Sorry for the simple question. Only recently I have been exploring PyQT.

1 Answers1

1

QTabWidget is based on QStackedWidget, which always uses the biggest size hint of all pages.

In order to avoid that, you can override both sizeHint and minimumSizeHint and ensure that it only returns the current widget hint.

Then, since resizing require some internal event handling, you can ensure that the application does that by calling processEvents() and then call adjustSize.

class ResizingTabWidget(QTabWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.currentChanged.connect(self.updateGeometry)

    def minimumSizeHint(self):
        return self.sizeHint()

    def sizeHint(self):
        current = self.currentWidget()
        if not current:
            return super().sizeHint()
        return current.sizeHint()


class MainWindow(QMainWindow):
    # ...
    def setupMainUi(self):
        # ...
        self.tabs = ResizingTabWidget()
        # ...

    def updateSizes(self):
        QApplication.processEvents()
        self.adjustSize()

Note that you don't need to call updateSizes in the __init__.
Also, be aware that from the UX perspective this is not a very good approach, as UIs that continuously resize themselves are considered annoying by many and often confuse the user.

musicamante
  • 41,230
  • 6
  • 33
  • 58
  • Appreciate the answer! I try adding the solution but the issue still happens. The window is resized when tab1 -> tab2 but the same doesn't happen when tab1 is resumed. I noticed in your answer you don't change the QSizePolicy. Isn't this necessary? Thanks – Leonardo Ferreira May 04 '21 at 14:29
  • @LeonardoFerreira you're not trying to add your code to `updateSizes`, right? It should only have the two lines specified above. Also, the size policies shouldn't be altered for this. – musicamante May 04 '21 at 17:34
  • @musicamante Where is `updateSizes` called? – Umbral Reaper May 05 '21 at 22:19
  • @UmbralReaper when the `tab` QTabWidget sends its `currentChanged` signal, see the original code. – musicamante May 05 '21 at 22:33
  • @musicamante That makes sense. I do admit that I tend to glaze over large blocks of code. – Umbral Reaper May 05 '21 at 22:39