QTabWidget 访问实际选项卡(不是内容小部件)
QTabWidget Access Actual Tab (not the content widget)
在此图片中:
我想访问实际的选项卡,而不是内容,所以我可以在鼠标悬停在实际选项卡上时设置 QPropertyAnimation
。我知道如何使悬停事件正常工作,并且可以在悬停时获取选项卡索引,但当我将鼠标悬停在其上时我无法访问实际的选项卡。某处是否有选项卡列表作为 QTabBar
或 QTabWidget
的属性,或者我在哪里可以找到这些选项卡?或者我是否必须子类化 addTab
函数才能单独创建选项卡?
额外信息
- 使用 PyQt5.14.1
- Windows 10
- Python 3.8.0
您无法访问 "tabs",因为它们不是对象,而是标签栏列表内容的抽象表示。
自定义其外观的唯一方法是子类化 QTabBar 并覆盖 paintEvent()
。
为了添加过度效果,您必须为每个选项卡提供独特的动画,因此您必须跟踪所有插入或删除的选项卡。 addTab
、insertTab
和 removeTab
方法不是有效选项,因为 QTabWidget 不使用它们。它使用 tabInserted()
and tabRemoved()
,因此它们也将被覆盖。
不过,这可能是样式表的问题,尤其是当您要设置字体或边距时。
幸运的是,我们可以使用带有自定义 PyQt 属性的 qproperty-*
声明,在下面的示例中,我将它们用于选项卡颜色。
class AnimatedTabBar(QtWidgets.QTabBar):
def __init__(self, *args):
super().__init__(*args)
palette = self.palette()
self._normalColor = palette.color(palette.Dark)
self._hoverColor = palette.color(palette.Mid)
self._selectedColor = palette.color(palette.Light)
self.animations = []
self.lastHoverTab = -1
@QtCore.pyqtProperty(QtGui.QColor)
def normalColor(self):
return self._normalColor
@normalColor.setter
def normalColor(self, color):
self._normalColor = color
for ani in self.animations:
ani.setEndValue(color)
@QtCore.pyqtProperty(QtGui.QColor)
def hoverColor(self):
return self._hoverColor
@hoverColor.setter
def hoverColor(self, color):
self._hoverColor = color
for ani in self.animations:
ani.setStartValue(color)
@QtCore.pyqtProperty(QtGui.QColor)
def selectedColor(self):
return self._selectedColor
@selectedColor.setter
def selectedColor(self, color):
self._selectedColor = color
self.update()
def tabInserted(self, index):
super().tabInserted(index)
ani = QtCore.QVariantAnimation()
ani.setStartValue(self.normalColor)
ani.setEndValue(self.hoverColor)
ani.setDuration(150)
ani.valueChanged.connect(self.update)
self.animations.insert(index, ani)
def tabRemoved(self, index):
super().tabRemoved(index)
ani = self.animations.pop(index)
ani.stop()
ani.deleteLater()
def event(self, event):
if event.type() == QtCore.QEvent.HoverMove:
tab = self.tabAt(event.pos())
if tab != self.lastHoverTab:
if self.lastHoverTab >= 0:
lastAni = self.animations[self.lastHoverTab]
lastAni.setDirection(lastAni.Backward)
lastAni.start()
if tab >= 0:
ani = self.animations[tab]
ani.setDirection(ani.Forward)
ani.start()
self.lastHoverTab = tab
elif event.type() == QtCore.QEvent.Leave:
if self.lastHoverTab >= 0:
lastAni = self.animations[self.lastHoverTab]
lastAni.setDirection(lastAni.Backward)
lastAni.start()
self.lastHoverTab = -1
return super().event(event)
def paintEvent(self, event):
selected = self.currentIndex()
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
style = self.style()
fullTabRect = QtCore.QRect()
tabList = []
for i in range(self.count()):
tab = QtWidgets.QStyleOptionTab()
self.initStyleOption(tab, i)
tabRect = self.tabRect(i)
fullTabRect |= tabRect
if i == selected:
# make the selected tab slightly bigger, but ensure that it's
# still within the tab bar rectangle if it's the first or the last
tabRect.adjust(
-2 if i else 0, 0,
2 if i < self.count() - 1 else 0, 1)
pen = QtCore.Qt.lightGray
brush = self._selectedColor
else:
tabRect.adjust(1, 1, -1, 1)
pen = QtCore.Qt.NoPen
brush = self.animations[i].currentValue()
tabList.append((tab, tabRect, pen, brush))
# move the selected tab to the end, so that it can be painted "over"
if selected >= 0:
tabList.append(tabList.pop(selected))
# ensure that we don't paint over the tab base
margin = max(2, style.pixelMetric(style.PM_TabBarBaseHeight))
qp.setClipRect(fullTabRect.adjusted(0, 0, 0, -margin))
for tab, tabRect, pen, brush in tabList:
qp.setPen(pen)
qp.setBrush(brush)
qp.drawRoundedRect(tabRect, 4, 4)
style.drawControl(style.CE_TabBarTabLabel, tab, qp, self)
class Example(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.tabWidget = QtWidgets.QTabWidget()
layout.addWidget(self.tabWidget)
self.tabBar = AnimatedTabBar(self.tabWidget)
self.tabWidget.setTabBar(self.tabBar)
self.tabWidget.addTab(QtWidgets.QCalendarWidget(), 'tab 1')
self.tabWidget.addTab(QtWidgets.QTableWidget(4, 8), 'tab 2')
self.tabWidget.addTab(QtWidgets.QGroupBox('Group'), 'tab 3')
self.tabWidget.addTab(QtWidgets.QGroupBox('Group'), 'tab 4')
self.setStyleSheet('''
QTabBar {
qproperty-hoverColor: rgb(128, 150, 140);
qproperty-normalColor: rgb(150, 198, 170);
qproperty-selectedColor: lightgreen;
}
''')
一些最后的说明:
- 我只实现了顶部标签栏方向,如果你想在其他方向使用标签,你需要改变边距和矩形调整;
- 请记住,使用样式表会破坏箭头按钮的外观;(当标签超出标签栏的宽度时),您需要仔细设置它们
- 可移动(可拖动)选项卡的绘制已损坏;
- 现在我真的不知道如何解决这个问题;
在此图片中:
我想访问实际的选项卡,而不是内容,所以我可以在鼠标悬停在实际选项卡上时设置 QPropertyAnimation
。我知道如何使悬停事件正常工作,并且可以在悬停时获取选项卡索引,但当我将鼠标悬停在其上时我无法访问实际的选项卡。某处是否有选项卡列表作为 QTabBar
或 QTabWidget
的属性,或者我在哪里可以找到这些选项卡?或者我是否必须子类化 addTab
函数才能单独创建选项卡?
额外信息
- 使用 PyQt5.14.1
- Windows 10
- Python 3.8.0
您无法访问 "tabs",因为它们不是对象,而是标签栏列表内容的抽象表示。
自定义其外观的唯一方法是子类化 QTabBar 并覆盖 paintEvent()
。
为了添加过度效果,您必须为每个选项卡提供独特的动画,因此您必须跟踪所有插入或删除的选项卡。 addTab
、insertTab
和 removeTab
方法不是有效选项,因为 QTabWidget 不使用它们。它使用 tabInserted()
and tabRemoved()
,因此它们也将被覆盖。
不过,这可能是样式表的问题,尤其是当您要设置字体或边距时。
幸运的是,我们可以使用带有自定义 PyQt 属性的 qproperty-*
声明,在下面的示例中,我将它们用于选项卡颜色。
class AnimatedTabBar(QtWidgets.QTabBar):
def __init__(self, *args):
super().__init__(*args)
palette = self.palette()
self._normalColor = palette.color(palette.Dark)
self._hoverColor = palette.color(palette.Mid)
self._selectedColor = palette.color(palette.Light)
self.animations = []
self.lastHoverTab = -1
@QtCore.pyqtProperty(QtGui.QColor)
def normalColor(self):
return self._normalColor
@normalColor.setter
def normalColor(self, color):
self._normalColor = color
for ani in self.animations:
ani.setEndValue(color)
@QtCore.pyqtProperty(QtGui.QColor)
def hoverColor(self):
return self._hoverColor
@hoverColor.setter
def hoverColor(self, color):
self._hoverColor = color
for ani in self.animations:
ani.setStartValue(color)
@QtCore.pyqtProperty(QtGui.QColor)
def selectedColor(self):
return self._selectedColor
@selectedColor.setter
def selectedColor(self, color):
self._selectedColor = color
self.update()
def tabInserted(self, index):
super().tabInserted(index)
ani = QtCore.QVariantAnimation()
ani.setStartValue(self.normalColor)
ani.setEndValue(self.hoverColor)
ani.setDuration(150)
ani.valueChanged.connect(self.update)
self.animations.insert(index, ani)
def tabRemoved(self, index):
super().tabRemoved(index)
ani = self.animations.pop(index)
ani.stop()
ani.deleteLater()
def event(self, event):
if event.type() == QtCore.QEvent.HoverMove:
tab = self.tabAt(event.pos())
if tab != self.lastHoverTab:
if self.lastHoverTab >= 0:
lastAni = self.animations[self.lastHoverTab]
lastAni.setDirection(lastAni.Backward)
lastAni.start()
if tab >= 0:
ani = self.animations[tab]
ani.setDirection(ani.Forward)
ani.start()
self.lastHoverTab = tab
elif event.type() == QtCore.QEvent.Leave:
if self.lastHoverTab >= 0:
lastAni = self.animations[self.lastHoverTab]
lastAni.setDirection(lastAni.Backward)
lastAni.start()
self.lastHoverTab = -1
return super().event(event)
def paintEvent(self, event):
selected = self.currentIndex()
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
style = self.style()
fullTabRect = QtCore.QRect()
tabList = []
for i in range(self.count()):
tab = QtWidgets.QStyleOptionTab()
self.initStyleOption(tab, i)
tabRect = self.tabRect(i)
fullTabRect |= tabRect
if i == selected:
# make the selected tab slightly bigger, but ensure that it's
# still within the tab bar rectangle if it's the first or the last
tabRect.adjust(
-2 if i else 0, 0,
2 if i < self.count() - 1 else 0, 1)
pen = QtCore.Qt.lightGray
brush = self._selectedColor
else:
tabRect.adjust(1, 1, -1, 1)
pen = QtCore.Qt.NoPen
brush = self.animations[i].currentValue()
tabList.append((tab, tabRect, pen, brush))
# move the selected tab to the end, so that it can be painted "over"
if selected >= 0:
tabList.append(tabList.pop(selected))
# ensure that we don't paint over the tab base
margin = max(2, style.pixelMetric(style.PM_TabBarBaseHeight))
qp.setClipRect(fullTabRect.adjusted(0, 0, 0, -margin))
for tab, tabRect, pen, brush in tabList:
qp.setPen(pen)
qp.setBrush(brush)
qp.drawRoundedRect(tabRect, 4, 4)
style.drawControl(style.CE_TabBarTabLabel, tab, qp, self)
class Example(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.tabWidget = QtWidgets.QTabWidget()
layout.addWidget(self.tabWidget)
self.tabBar = AnimatedTabBar(self.tabWidget)
self.tabWidget.setTabBar(self.tabBar)
self.tabWidget.addTab(QtWidgets.QCalendarWidget(), 'tab 1')
self.tabWidget.addTab(QtWidgets.QTableWidget(4, 8), 'tab 2')
self.tabWidget.addTab(QtWidgets.QGroupBox('Group'), 'tab 3')
self.tabWidget.addTab(QtWidgets.QGroupBox('Group'), 'tab 4')
self.setStyleSheet('''
QTabBar {
qproperty-hoverColor: rgb(128, 150, 140);
qproperty-normalColor: rgb(150, 198, 170);
qproperty-selectedColor: lightgreen;
}
''')
一些最后的说明:
- 我只实现了顶部标签栏方向,如果你想在其他方向使用标签,你需要改变边距和矩形调整;
- 请记住,使用样式表会破坏箭头按钮的外观;(当标签超出标签栏的宽度时),您需要仔细设置它们
- 可移动(可拖动)选项卡的绘制已损坏;
- 现在我真的不知道如何解决这个问题;