如何制作类似 chrome 浏览器的标签

How to make tabs like chrome browser

我已经创建了一个浏览器,但是当标签到达UI的最后一个标签时,Tab不像chrome那样倒塌,但仍保持相同的宽度。

我想要什么?

我想要登录到最后一个选项卡时应该像chrome浏览器一样折叠。

我想要的。在上图中,选项卡的宽度保持不变,没有折叠或收缩...但在下图中,随着我们添加更多选项卡

,chrome 浏览器中的选项卡逐渐缩小

QTabBar不支持,需要自己实现。

解决方案使用 tabSizeHint(),return 的尺寸通常适合每个选项卡的内容,但在这种情况下,您需要 return 默认的标准尺寸和更小的尺寸每当可用 space 少于所需时。

请注意,由于调整选项卡的大小会再次调用 tabSizeHint()minimumTabSizeHint(),您需要添加一个系统来避免无限递归。

当前的 QStyle 和字体规格用于计算最小和默认大小,标签小部件需要在调整大小时更新标签栏的最佳大小。

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)