使用 PyQt6,我尝试显示以
QIcon(QPixmap)
为中心的图像,没有带有 QComboBox.addItem()
的文本。
> help(QComboBox.addItem)
Help on built-in function addItem:
addItem(...) method of PyQt6.sip.wrappertype instance
addItem(self, str, userData: Any = None)
addItem(self, QIcon, str, userData: Any = None)
到目前为止,我认为我必须使用
QStyledItemDelegate
作为下拉列表,但我仍然使用以下方法得到了一个奇怪的结果:
class MyComboBox(QComboBox):
def __init__(self, *args, **kwargs):
super().__init__()
delegate = AlignDelegate(self)
self.setItemDelegate(delegate)
class AlignDelegate(QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
option.features &= ~QStyleOptionViewItem.ViewItemFeature.HasDisplay
option.decorationAlignment = Qt.AlignmentFlag.AlignCenter
option.decorationPosition = QStyleOptionViewItem.Position.Top
下拉QIcon水平居中,但垂直居中,但我尝试使用以下行删除文本,但除非字符串是
""
,否则文本仍然在某处。
option.features &= ~QStyleOptionViewItem.ViewItemFeature.HasDisplay
所选行也是如此,我不知道如何没有文本(除了
""
)并让 QIcon 居中而不是文本。
class MyComboBox(QComboBox):
def __init__(self, *args, **kwargs):
super().__init__()
self.setEditable(True)
self.lineEdit().setReadOnly(True)
self.lineEdit().setAlignment(Qt.AlignmentFlag.AlignCenter)
玩起来有两个问题:
由于 Qt 小部件(包括委托)的大多数功能始终依赖于当前样式,这意味着确保图标始终居中的唯一方法是重写委托的
paint()
函数。
请注意,我们应该始终尝试遵循默认行为:QComboBox 的默认委托是 QItemDelegate,而不是 QStyledItemDelegate。
QItemDelegate 对于项目的每个元素都有单独的函数;因为我们只想显示图标,所以我们只需要调用
drawBackground()
(它也绘制突出显示项目的选定状态),然后绘制图标。
class AlignDelegate(QItemDelegate):
def drawCenteredIcon(self, qp, opt, index):
icon = index.data(Qt.ItemDataRole.DecorationRole)
if isinstance(icon, QIcon):
if not opt.state & QStyle.StateFlag.State_Enabled:
mode = QIcon.Mode.Disabled
elif opt.state & QStyle.StateFlag.State_Selected:
mode = QIcon.Mode.Selected
else:
mode = QIcon.Mode.Normal
icon = icon.pixmap(opt.rect.size(), mode)
rect = opt.rect
elif isinstance(icon, QPixmap):
rect = QRect(QPoint(), icon.size().scaled(opt.rect.size(),
Qt.AspectRatioMode.KeepAspectRatio))
rect.moveCenter(opt.rect.center())
else:
return
qp.drawPixmap(rect, icon)
def paint(self, qp, opt, index):
self.drawBackground(qp, opt, index)
self.drawCenteredIcon(qp, opt, index)
def sizeHint(self, opt, index):
# return the default size
return super().sizeHint(opt, QModelIndex())
如果你还想使用QStyledItemDelegate的功能(包括样式表),那么我们可以通过调用样式的相关函数来实现类似的方法,即
drawPrimitive()
和PE_PanelItemViewItem
。
class StyledAlignDelegate(QStyledItemDelegate):
...
def paint(self, qp, opt, index):
self.initStyleOption(opt, index)
style = opt.widget.style()
style.drawPrimitive(QStyle.PrimitiveElement.PE_PanelItemViewItem,
opt, qp, opt.widget)
self.drawCenteredIcon(qp, opt, index)
...
如上所述,当弹出窗口不可见时,委托对组合框的显示方式没有影响。
为了更改这种显示,我们需要通过覆盖
paintEvent()
来遵循 QComboBox 的默认行为,并将原始 C++ 代码“移植”到 Python 中。
图标的正确居中是通过使用 subControlRect()
子控件调用当前样式
SC_ComboBoxEditField
函数来实现的,这确保了我们获得显示“标签”的正确矩形,不包括箭头按钮组合框。
请注意,这意味着我们可能会破坏默认行为,以防任何未来的 Qt 版本改变默认实现,这可能是由于错误报告/修复、QComboBox 绘制的更改甚至 QStyle 实现造成的。
class MyComboBox(QComboBox):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setItemDelegate(AlignDelegate(self))
# alternatively, for the styled delegate
# self.setItemDelegate(StyledAlignDelegate(self))
def paintEvent(self, event):
qp = QStylePainter(self)
qp.setPen(self.palette().color(QPalette.ColorRole.Text))
opt = QStyleOptionComboBox()
self.initStyleOption(opt)
if self.currentIndex() < 0 and self.placeholderText():
opt.palette.setBrush(QPalette.ColorRole.ButtonText,
opt.palette.placeholderText())
opt.currentText = self.placeholderText()
elif opt.currentText:
opt.currentText = ''
style = self.style()
qp.drawComplexControl(style.ComplexControl.CC_ComboBox, opt)
if not opt.currentIcon.isNull():
rect = style.subControlRect(
style.ComplexControl.CC_ComboBox, opt,
style.SubControl.SC_ComboBoxEditField, self
)
style.drawItemPixmap(qp, rect, Qt.AlignmentFlag.AlignCenter,
opt.currentIcon.pixmap(rect.size()))
请注意,如果您担心未来的变化(如上所述),您可以实施安全措施:只需为装饰创建一个等于或高于
Qt::UserRole
的自定义数据角色,将其用于中的 index.data()
getter drawCenteredIcon
功能,并始终通过使用 setItemData(index, icon, customrole)
设置图标来添加新项目。paintEvent()
覆盖 super().paintEvent(event)
(不会绘制图标或文本),然后最终使用该图标/像素图进行图标绘制,如上面最后一个代码的最后部分所示自定义角色。