如何制作类似 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)
我已经创建了一个浏览器,但是当标签到达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)