pythonqtpyqtqtabwidget

PyQt Hide Contents of QTabWidget but keep QTabBar visible


I was wondering if it is possible in PyQt, when trying to hide QTabWidget, to hide the contents themselves, but keep the QTabBar visible.

As of now, I have custom TabBar and TabWidget that inherit from QTabBar and QTabWidget. My TabBar is vertical and on the west side of the TabWidget.

I have created the button and connected a slot to it that is supposed to hide the TabWidget. Issue is, hiding the TabWidget hides the TabBar, which I do not want.

I have tried overriding .hide() method, tried

self.tab_widget.hide()
self.tab_widget.tabBar().show()

But nothing works, since the TabWidget is the parent of TabBar, hiding TabWidget, automatically hides TabBar.

Of course I could probably construct my own poor man's TabWidget from two widgets so that I could hide and show the widgets whenever I want, but inhereting from QTabWidget is very convenient and I do not want to do it from scratch.

I also upon hiding could just resize tab widget to the width of the TabBar, but I feel that is stupid solution as well.

My Idea is to have an expandable/shrinkable and hideable sidebar, such as in VSCode. enter image description here

Solutions in C++ QT will be helpful as well, as I understand both, and it is not necessarily Python issue, but Qt.


Solution

  • QTabWidget is a composite widget, made of a QTabBar and a QStackedWidget.

    Most importantly, it overrides basic aspects that are required for layout management, such as sizeHint(), minimumSizeHint() and sizePolicy().

    You cannot just toggle the visibility of the tab bar (nor the stacked widget contents):

    1. as you already found out, hiding the QTabWidget and showing the tab bar is fundamentally a no-op, since the parent visibility has precedence: calling show() or setVisible(True) on a nested child (a QWidget without a Qt.Window window flag) will only make it visible as long as the parent is shown;
    2. trying to hide the stacked widget is irrelevant, because the QTabWidget size hint functions will always consider the stacked widget hints, no matter the visibility;

    This is a possible implementation that toggles visibility of the tab "pages" whenever a tab is clicked on the currently visible page (if any): clicking on a currently selected and visible tab will always hide the contents, while contents will be made visible when clicking on any tab, even if no contents are visible.

    from PyQt5.QtWidgets import *
    
    class ToggleTabWidget(QTabWidget):
        _contentsVisible = True
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
            self.tabBarClicked.connect(self.toggleContentsVisibleFromClick)
            self._stackedWidget = self.findChild(QStackedWidget)
    
        def toggleContentsVisibleFromClick(self, index):
            if not self._contentsVisible or index == self.currentIndex():
                self.toggleContentsVisibility()
    
        def toggleContentsVisibility(self):
            self.setContentsVisible(not self._contentsVisible)
    
        def setContentsVisible(self, visible):
            if self._contentsVisible != visible:
                self._contentsVisible = visible
                self._stackedWidget.setVisible(visible)
                self.setDocumentMode(not visible)
    
                tabHint = self.tabBar().sizeHint()
                maxSize = 16777215 # default maximum size
                if not self.tabPosition() & self.West:
                    # horizontal
                    if not visible:
                        maxSize = tabHint.height()
                    self.setMaximumHeight(maxSize)
                else:
                    if not visible:
                        maxSize = tabHint.width()
                    self.setMaximumWidth(maxSize)
    
    
    class Test(QWidget):
        def __init__(self):
            super().__init__()
            self.tabWidget = ToggleTabWidget()
            self.tabWidget.addTab(QTextEdit('hello world'), 'Hello')
            self.tabWidget.addTab(QTextEdit('bye world'), 'Bye')
            self.table = QTableWidget(10, 2)
    
            layout = QVBoxLayout(self)
            layout.addWidget(self.tabWidget)
            layout.addWidget(self.table)
    
    
    app = QApplication([])
    test = Test()
    test.show()
    app.exec()
    

    Note that: