0

I'm using QTabWidget to show content with different sizes. How do I resize my tabs in a way that it matches my content?

Goal:

Tab1:

Tab1

Tab2:

Tab2

I tried to write a function that connects to QTabWidget.currentChanged(), I managed to ignore QSizePolicy, but the resize doesn't come into effect. I can however resize manually without problem.

import sys


from PyQt5.QtWidgets import (
    QApplication,
    QCheckBox
    QTabWidget,
    QVBoxLayout,
    QHBoxLayout,
    QWidget,
    QLabel,
    QLineEdit,
    QSizePolicy
)


class Window(QWidget):
    def curTabChanged(self,index):
        for i in range(self.tabs.count()):
            if i == index:
                self.tabs.widget(i).setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Preferred)
            else:
                self.tabs.widget(i).setSizePolicy(QSizePolicy.Ignored,QSizePolicy.Ignored)
        #self.tabs.resize(self.tabs.widget(index).minimumSizeHint())
        self.tabs.widget(index).resize(100,100)
        self.tabs.widget(index).adjustSize()
        #self.resize(self.minimumSizeHint())
        self.resize(100,100)
        self.adjustSize()
    
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Define Volumes")
        self.resize(300, 100)
        # Create a top-level layout
        layout = QVBoxLayout()
        self.setLayout(layout)
        # Create the tab widget with two tabs
        self.tabs = QTabWidget()
        self.tabs.addTab(self.generalTabUI(), "Input")
        self.tabs.addTab(self.helpTabUI(), "Help")
        self.tabs.currentChanged.connect(self.curTabChanged)
        self.curTabChanged(0)
        layout.addWidget(self.tabs)
        self.setLayout(layout)

    def generalTabUI(self):

        """Create the General page UI."""
        generalTab = QWidget()
        layout = QHBoxLayout()
        self.label_text_left = QLabel("1-")
        self.label_text_right = QLabel("-100")
        self.textbox = QLineEdit()
        layout.addWidget(self.label_text_left)
        layout.addWidget(self.textbox)
        layout.addWidget(self.label_text_right)
        generalTab.setLayout(layout)
        return generalTab

    def helpTabUI(self):
        helpTab = QWidget()
        layout = QVBoxLayout()
        self.label = QLabel(self)
        self.label.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit,\n\nsed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\n\n quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \n\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \n\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")

        layout.addWidget(self.label)
        helpTab.setLayout(layout)
        return helpTab

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241

2 Answers2

2

QTabWidget, like QStackedWidget, uses QStackedLayout, which always has a minimum size hint computed using the minimum size hint of all of its widgets.

In order to prevent that, both sizeHint() and minimumSizeHint() should be reimplemented, and updateGeometry() should be called whenever the index changes.

In the following example I've unified the two functions, converted from :sizeHint() of QTabWidget, but in order to provide correct implementation, the original :minimumSizeHint() implementation should be adapted too.

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

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

    def sizeHint(self):
        lc = QSize(0, 0)
        rc = QSize(0, 0)
        opt = QStyleOptionTabWidgetFrame()
        self.initStyleOption(opt)
        if self.cornerWidget(Qt.TopLeftCorner):
            lc = self.cornerWidget(Qt.TopLeftCorner).sizeHint()
        if self.cornerWidget(Qt.TopRightCorner):
            rc = self.cornerWidget(Qt.TopRightCorner).sizeHint()
        layout = self.findChild(QStackedLayout)
        layoutHint = layout.currentWidget().sizeHint()
        tabHint = self.tabBar().sizeHint()
        if self.tabPosition() in (self.North, self.South):
            size = QSize(
                max(layoutHint.width(), tabHint.width() + rc.width() + lc.width()), 
                layoutHint.height() + max(rc.height(), max(lc.height(), tabHint.height()))
            )
        else:
            size = QSize(
                layoutHint.width() + max(rc.width(), max(lc.width(), tabHint.width())), 
                max(layoutHint.height(), tabHint.height() + rc.height() + lc.height())
            )
        return size


class Window(QWidget):
    def __init__(self):
        # ...
        # no need to for a specific function, just directly call adjustSize()
        self.tabs.currentChanged.connect(self.adjustSize)

Two small suggestions. 1) Avoid unnecessary line spacings in your code, as it is distracting. 2) __init__ should preferably go at the beginning of the class, without any custom function before it: it's the "initialization", and it should not go after other things.

musicamante
  • 41,230
  • 6
  • 33
  • 58
  • Thank you very much, I'm really new to this stuff. I found a hacky fix to get my Tabs to resize vertically, can you take a look at it? I used a trick from this post https://stackoverflow.com/questions/28660960/resize-qmainwindow-to-minimal-size-after-content-of-layout-changes. – gilles colling Feb 04 '21 at 22:06
  • This post got closed: I posted a new question here: https://stackoverflow.com/questions/66054857/resized-text-in-custom-resized-qtabwidget-is-cut-off – gilles colling Feb 04 '21 at 22:36
  • 1
    I strongly suggest you to carefully study my code and the documentation about all classes and functions used. It's not immediate stuff, and you can't expect to fix in a few minutes. Also, QLabel has some issues in layouts due to the fact that it can containt rich text and have adpting size (see [Layout issues](https://doc.qt.io/qt-5/layout.html#layout-issues)). – musicamante Feb 04 '21 at 23:37
0

I managed a hacky fix for my Code:

import sys

from PyQt5.QtCore import (
    QSize
    )

from PyQt5.QtWidgets import (
    QApplication,
    QCheckBox,
    QTabWidget,
    QVBoxLayout,
    QHBoxLayout,
    QWidget,
    QLabel,
    QLineEdit,
    QSizePolicy,
)

class Window(QWidget):
    
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Define Volumes")
        # Create a top-level layout
        layout = QVBoxLayout ()
        # Create the tab widget with two tabs
        self.tabs = QTabWidget()
        self.tabs.addTab(self.generalTabUI(), "Input")
        self.tabs.addTab(self.helpTabUI(), "Help")
        self.tabs.currentChanged.connect(self.curTabChanged)
        self.curTabChanged(0)
        layout.addWidget(self.tabs)
        self.setLayout(layout)
        self.rsz()

    def curTabChanged(self,index):
        for i in range(self.tabs.count()):
            self.tabs.widget(i).setSizePolicy(QSizePolicy.Ignored,QSizePolicy.Ignored)
        widget = self.tabs.currentWidget()
        widget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        const_width = self.tabs.widget(1).minimumSizeHint().width()
        widget.setFixedWidth(const_width)
        self.rsz()

    def rsz(self):
        for i in range(0,10):
            QApplication.processEvents()
        return self.resize(self.minimumSizeHint())

    def generalTabUI(self):
        """Create the General page UI."""
        generalTab = QWidget()
        layout = QHBoxLayout()
        self.label_text_left = QLabel("1-")
        self.label_text_right = QLabel("-100")
        self.textbox = QLineEdit()
        layout.addWidget(self.label_text_left)
        layout.addWidget(self.textbox)
        layout.addWidget(self.label_text_right)
        generalTab.setLayout(layout)
        return generalTab


    def helpTabUI(self):
        helpTab = QWidget()
        layout = QVBoxLayout()
        self.label = QLabel(self)
        self.label.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit,\n\nsed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\n\n quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \n\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \n\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
        layout.addWidget(self.label)
        helpTab.setLayout(layout)
        return helpTab


if __name__ == "__main__":

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