Search code examples
pythonpython-3.xpyqtqtablewidget

How to make tabs like chrome browser


I have created a browser but when tab reached to the last of the UI the tab not collapse like chrome but still remain in same width.

What I want?

I want when tab reached to last the tab should be collapse like chrome browser.

What I want. In the above pic the width of the tabs remain same not collapse or shrink... but in the below pic the tabs in the chrome browser shrink gradually as we add more tabs

enter image description here


Solution

  • QTabBar doesn't support that, and you need to implement it on your own.

    The solution uses tabSizeHint(), which returns a size that normally suits the contents of each tab, but in this case you need to return a default standard size and a smaller one whenever the available space is less than required.

    Note that since resizing the tabs calls again both tabSizeHint() and minimumTabSizeHint(), you need to add a system that avoids infinite recursion.

    The current QStyle and font metrics are used to compute the minimum and default sizes, and the tab widget needs to update the optimal size of the tab bar whenever it's resized.

    class ShrinkTabBar(QtWidgets.QTabBar):
        _widthHint = -1
        _initialized = False
        _recursiveCheck = False
        addClicked = QtCore.pyqtSignal()
        def __init__(self, parent):
            super().__init__(parent)
            self.setElideMode(QtCore.Qt.ElideRight)
            self.setExpanding(False)
            self.setTabsClosable(True)
            self.addButton = QtWidgets.QToolButton(self.parent(), text='+')
            self.addButton.clicked.connect(self.addClicked)
            self._recursiveTimer = QtCore.QTimer(singleShot=True, timeout=self._unsetRecursiveCheck, interval=0)
            self._closeIconTimer = QtCore.QTimer(singleShot=True, timeout=self._updateClosable, interval=0)
    
        def _unsetRecursiveCheck(self):
            self._recursiveCheck = False
    
        def _updateClosable(self):
            self.setTabsClosable(self._widthHint >= self._minimumCloseWidth)
    
        def _computeHints(self):
            if not self.count() or self._recursiveCheck:
                return
            self._recursiveCheck = True
    
            opt = QtWidgets.QStyleOptionTab()
            self.initStyleOption(opt, 0)
            width = self.style().pixelMetric(QtWidgets.QStyle.PM_TabBarTabHSpace, opt, self)
            iconWidth = self.iconSize().width() + 4
            self._minimumWidth = width + iconWidth
    
            # default text widths are arbitrary
            fm = self.fontMetrics()
            self._minimumCloseWidth = self._minimumWidth + fm.width('x' * 4) + iconWidth
            self._defaultWidth = width + fm.width('x' * 17)
            self._defaultHeight = super().tabSizeHint(0).height()
            self._minimumHint = QtCore.QSize(self._minimumWidth, self._defaultHeight)
            self._defaultHint = self._tabHint = QtCore.QSize(self._defaultWidth, self._defaultHeight)
    
            self._initialized = True
            self._recursiveTimer.start()
    
        def _updateSize(self):
            if not self.count():
                return
            frameWidth = self.style().pixelMetric(
                QtWidgets.QStyle.PM_DefaultFrameWidth, None, self.parent())
            buttonWidth = self.addButton.sizeHint().width()
            self._widthHint = (self.parent().width() - frameWidth - buttonWidth) // self.count()
            self._tabHint = QtCore.QSize(min(self._widthHint, self._defaultWidth), self._defaultHeight)
            # dirty trick to ensure that the layout is updated
            if not self._recursiveCheck:
                self._recursiveCheck = True
                self.setIconSize(self.iconSize())
                self._recursiveTimer.start()
    
        def minimumTabSizeHint(self, index):
            if not self._initialized:
                self._computeHints()
            return self._minimumHint
    
        def tabSizeHint(self, index):
            if not self._initialized:
                self._computeHints()
            return self._tabHint
    
        def tabLayoutChange(self):
            if self.count() and not self._recursiveCheck:
                self._updateSize()
                self._closeIconTimer.start()
    
        def tabRemoved(self, index):
            if not self.count():
                self.addButton.setGeometry(1, 2, 
                    self.addButton.sizeHint().width(), self.height() - 4)
    
        def changeEvent(self, event):
            super().changeEvent(event)
            if event.type() in (event.StyleChange, event.FontChange):
                self._updateSize()
    
        def resizeEvent(self, event):
            if not self.count():
                super().resizeEvent(event)
                return
            self._recursiveCheck = True
            super().resizeEvent(event)
            height = self.sizeHint().height()
            if height < 0:
                # a tab bar without tabs returns an invalid size
                height = self.addButton.height()
            self.addButton.setGeometry(self.geometry().right() + 1, 2, 
                self.addButton.sizeHint().width(), height - 4)
            self._closeIconTimer.start()
            self._recursiveTimer.start()
    
    
    class ShrinkTabWidget(QtWidgets.QTabWidget):
        addClicked = QtCore.pyqtSignal()
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self._tabBar = ShrinkTabBar(self)
            self.setTabBar(self._tabBar)
            self._tabBar.addClicked.connect(self.addClicked)
    
        def resizeEvent(self, event):
            self._tabBar._updateSize()
            super().resizeEvent(event)