我的目标是制作一个组合框,只有当鼠标悬停在它上面时才能显示向下箭头。为此,我将QComboBox子类化,并重新实现enterEvent和leaveEvent,这将相应地更改其样式表。它的工作原理和预期的一样,当窗口显示时,向下箭头被隐藏,当鼠标悬停在组合框上方时,向下箭号显示。然后,当鼠标单击组合框文本时,下拉列表会显示出来,向下箭头会再次变为不可见(我可以处理,但如果有人有解决方案,我会洗耳恭听(。主要问题是,当鼠标单击下拉箭头时,它不仅隐藏了下拉箭头,就像鼠标单击文本时那样,还隐藏了文本和下拉箭头。我做了一些测试来找出问题的原因,似乎在QApplication上调用setEffectEnabled(Qt.UIEffect.UI_AnimateCombo,False(是个问题,尽管我不知道禁用下拉动画与单击下拉箭头时使文本消失有什么关系。此外,如果有人有更优雅的方式来做我想做的事,我会洗耳恭听:(。下面是示例代码:
import sys
from PyQt5.QtCore import QEvent, Qt
from PyQt5.QtGui import QEnterEvent
from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox, QWidget
class TestComboBox(QComboBox):
def __init__(self, parent=None):
super().__init__(parent)
self.default_stylesheet = '''
QComboBox {
background-color: rgba(0, 0, 0, 0);
}
QComboBox QAbstractItemView {
background-color: white;
min-width: 150px;
}'''
self.hide_down_arrow_stylesheet = '''QComboBox::down-arrow {
background-color: rgba(0, 0, 0, 0);}'''
self.setStyleSheet(self.default_stylesheet + self.hide_down_arrow_stylesheet)
def enterEvent(self, event: QEnterEvent) -> None:
self.setStyleSheet(self.default_stylesheet)
super().enterEvent(event)
def leaveEvent(self, event: QEvent) -> None:
self.setStyleSheet(self.default_stylesheet + self.hide_down_arrow_stylesheet)
super().leaveEvent(event)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.widget = QWidget(self)
self.setCentralWidget(self.widget)
self.combobox = TestComboBox(self.widget)
self.combobox.resize(180, 30)
self.combobox.insertItem(0, "item 1")
self.combobox.insertItem(0, "item 2")
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setEffectEnabled(Qt.UIEffect.UI_AnimateCombo, False)
window = MainWindow()
sys.exit(app.exec())
可能存在一些内部和复杂的错误,因为您只设置了一个单个子控件属性,但文档专门针对这一方面:
注意:对于复杂的小部件,如QComboBox和QScrollBar,如果一个属性或子控件是自定义的,所有其他属性或子控制器也必须自定义。
请注意,在大多数情况下,这也与没有设置布局管理器的事实有关,在我的大多数测试中,只要使用了正确的布局,问题就会自动消失(正如您总是应该做的那样,即使您只使用一个子窗口小部件(。
class MainWindow(QMainWindow):
def __init__(self):
# ...
layout = QVBoxLayout(self.widget)
layout.addWidget(self.combobox)
现在,我仍然能够在某些条件下重现这个问题,即使有布局。这是由于一开始所解释的:如果您设置了一个复杂小部件的属性,则必须设置所有属性;不这样做可能会导致意外行为。
在这种特定的情况下,我认为QStyleSheetStyle(这是一种在任何受QSS影响的小部件上自动设置的私人样式,包括继承的小部件(无法恢复画家的笔,它使用箭头的颜色作为组合标签,但它只在特定(可能是"随机"(条件下这样做。
重点仍然是:有些属性没有设置,特别是drop-down
伪元素
这样做的结果是,为该伪元素设置border
会将箭头图像重置为空白图像,但我们可以将其用作实际优势;由于我们知道设置边界会导致无箭头图像,因此我们可以相应地更新样式表:
class TestComboBox(QComboBox):
default_stylesheet = '''
TestComboBox {
color: black;
selection-color: black;
}
TestComboBox QAbstractItemView {
background-color: white;
min-width: 150px;
}
'''
non_hover_stylesheet = '''
TestComboBox::drop-down {
border: none;
}
'''
def __init__(self, parent=None):
super().__init__(parent)
self.setHoverState(False)
def setHoverState(self, state):
qss = self.default_stylesheet
if not state:
qss += self.non_hover_stylesheet
self.setStyleSheet(qss)
def enterEvent(self, event):
super().enterEvent(event)
self.setHoverState(True)
def leaveEvent(self, event):
super().leaveEvent(event)
self.setHoverState(False)
进一步说明:
- 我删除了
selection-background-color
和background-color
属性,因为它们会导致与某些样式不一致的行为(特别是"Fusion"样式,它被认为是QSS的参考样式,应该始终检查(,因为这些属性经常被忽略alpha级别而使用;我不能用";WindowsVista";风格,但重点仍然是:如果你想要透明的背景颜色,它必须与实际背景一致;考虑使用QApplication的palette()
来获得Highlight
和HighlightedText
角色的颜色,并最终使用这些颜色(没有alpha级别(来设置实际背景 - 我制作了QSS";模板";作为类属性:因为我们可以假设样式表被设置为类默认值,所以将其定义为实例属性没有什么意义
- 这显然不是(也不能(考虑继承的样式表;虽然我特别使用了
TestComboBox
类选择器而不是QComboBox
,但请记住,如果您将样式表(扩展其他QComboBox属性或伪元素/状态(设置为任何父级或应用程序,您可能会得到不一致和意外的行为 - (不相关,但仍然很重要(在小部件的
__init__
内不调用self.show()
(或self.setVisible(True)
(是一种很好的做法
我通过阅读文档解决了这个问题https://doc.qt.io/qt-5/stylesheet-syntax.html#sub-控件。解决方案如下:
import sys
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication, QMainWindow, QComboBox, QWidget, QVBoxLayout
class TestComboBox(QComboBox):
def __init__(self, parent=None):
super().__init__(parent)
self.default_stylesheet = '''
QComboBox {
color: black;
selection-color: black;
selection-background-color: rgba(0, 0, 0, 0);
background-color: rgba(0, 0, 0, 0);
}
QComboBox QAbstractItemView {
background-color: white;
min-width: 150px;
}
QComboBox:!hover::down-arrow {
color: rgba(0, 0, 0, 0);
}
'''
self.setStyleSheet(self.default_stylesheet)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.widget = QWidget(self)
self.setCentralWidget(self.widget)
self.layout = QVBoxLayout(self.widget)
self.combobox = TestComboBox()
self.combobox.resize(180, 30)
self.combobox.insertItem(0, "item 1")
self.combobox.insertItem(0, "item 2")
self.combobox.insertItem(0, "item 3")
self.combobox.insertItem(0, "item 4")
self.combobox.insertItem(0, "item 5")
self.layout.addWidget(self.combobox)
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setEffectEnabled(Qt.UIEffect.UI_AnimateCombo, False)
window = MainWindow()
window.show()
sys.exit(app.exec())