PySide6自定义QTabBar绘制右侧文本后新标签文本不显示问题
问题:重写QTabBar的paintEvent后新标签文本空白
尝试将标签文本绘制在标签右侧以避免长名称被截断,但重写paintEvent后,新添加的标签仅显示空白,无法正常显示指定文本。
原代码
from PySide6 import QtWidgets from PySide6 import QtCore from PySide6.QtCore import Signal, QSize from PySide6.QtWidgets import QLabel, QWidget, QVBoxLayout, QStyle, QStyleOptionTab, QStylePainter class ShrinkTabBar(QtWidgets.QTabBar): _widthHint = -1 _initialized = False _recursiveCheck = False addClicked = Signal() def __init__(self, parent): super(ShrinkTabBar, self).__init__(parent) self.setElideMode(QtCore.Qt.TextElideMode.ElideLeft) self.setExpanding(False) self.setMovable(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._tabHint = QSize(0, 0) self._minimumHint = QSize(0, 0) def _unsetRecursiveCheck(self): self._recursiveCheck = False 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.PixelMetric.PM_TabBarTabHSpace, opt, self) iconWidth = self.iconSize().width() + 2 self._minimumWidth = width + iconWidth # default text widths are arbitrary fm = self.fontMetrics() self._minimumCloseWidth = self._minimumWidth + fm.horizontalAdvance('x' * 4) + iconWidth self._defaultWidth = width + fm.horizontalAdvance('x' * 26) 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.PixelMetric.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() # BUG: is somewhere in here def paintEvent(self, event): painter = QStylePainter(self) option = QStyleOptionTab() for index in range(self.count()): self.initStyleOption(option, index) tabRect = self.tabRect(index) tabRect.moveLeft(10) painter.drawControl(QStyle.ControlElement.CE_TabBarTabShape, option) painter.drawText(tabRect, QtCore.Qt.AlignmentFlag.AlignVCenter | QtCore.Qt.TextFlag.TextDontClip, self.tabText(index)) class ShrinkTabWidget(QtWidgets.QTabWidget): addClicked = Signal() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._tabBar = ShrinkTabBar(self) self.setTabBar(self._tabBar) self._tabBar.setTabsClosable(True) self._tabBar.addClicked.connect(self.get_page_choices) def get_page_choices(self): selection_page = QLabel("This worked") index = self.addTab(selection_page, 'New Tab') # BUG: New Tab does not show up only a blank self._tabBar.setCurrentIndex(index) # Set as current tab
问题分析与修复
问题根源
- 文本区域偏移错误:原代码中
tabRect.moveLeft(10)将文本绘制区域向左偏移10像素,导致文本被移出标签的可见范围,所以显示空白。 - 未处理关闭按钮:开启
setTabsClosable(True)后,原paintEvent没有绘制关闭按钮,也没有为关闭按钮预留空间,进一步挤压文本区域。 - 对齐方式不符合需求:仅设置
AlignVCenter垂直居中,但没有设置水平靠右,无法实现“文本在标签右侧”的效果。
修复后的paintEvent代码
def paintEvent(self, event): painter = QStylePainter(self) option = QStyleOptionTab() for index in range(self.count()): self.initStyleOption(option, index) tabRect = self.tabRect(index) # 计算关闭按钮宽度(如果开启了可关闭) closeBtnWidth = 0 if self.tabsClosable(): closeBtnWidth = self.style().pixelMetric(QStyle.PM_TabBarCloseButtonWidth, option, self) # 为文本区域预留左侧空间:图标宽度 + 关闭按钮宽度 + 边距 iconWidth = self.iconSize().width() textMargin = 8 textRect = tabRect.adjusted(iconWidth + closeBtnWidth + textMargin, 0, -textMargin, 0) # 绘制标签形状(保持系统原生样式) painter.drawControl(QStyle.CE_TabBarTabShape, option) # 绘制文本:垂直居中 + 水平靠右 + 不截断 painter.drawText(textRect, QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight | QtCore.Qt.TextDontClip, self.tabText(index)) # 绘制关闭按钮(如果需要) if self.tabsClosable(): # 调整关闭按钮的位置:左侧边距 + 垂直居中 closeBtnRect = QtCore.QRect( tabRect.left() + textMargin, tabRect.top() + (tabRect.height() - closeBtnWidth) // 2, closeBtnWidth, closeBtnWidth ) # 更新option的rect为关闭按钮区域,绘制按钮 option.rect = closeBtnRect painter.drawControl(QStyle.CE_TabBarTabCloseButton, option)
额外调整
确保_computeHints方法中计算的最小宽度包含关闭按钮的空间,避免标签宽度不足:
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.PixelMetric.PM_TabBarTabHSpace, opt, self) iconWidth = self.iconSize().width() + 2 self._minimumWidth = width + iconWidth # 增加关闭按钮宽度到最小宽度计算中 closeBtnWidth = self.style().pixelMetric(QStyle.PM_TabBarCloseButtonWidth, opt, self) if self.tabsClosable() else 0 fm = self.fontMetrics() self._minimumCloseWidth = self._minimumWidth + fm.horizontalAdvance('x' * 4) + iconWidth + closeBtnWidth self._defaultWidth = width + fm.horizontalAdvance('x' * 26) + closeBtnWidth self._defaultHeight = super().tabSizeHint(0).height() self._minimumHint = QtCore.QSize(self._minimumWidth + closeBtnWidth, self._defaultHeight) self._defaultHint = self._tabHint = QtCore.QSize(self._defaultWidth, self._defaultHeight) self._initialized = True self._recursiveTimer.start()
修改后,新添加的标签文本会正常显示在标签右侧,同时保留关闭按钮功能,不会出现文本截断或空白问题。
内容的提问来源于stack exchange,提问作者David Frick
相关产品推荐
相关产品推荐

