Using PyQt6, I'm trying to display an image with QIcon(QPixmap) centered with no text with a 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)
So far, I've figured I had to play with QStyledItemDelegate for the dropdown list yet I still get to a meh result using following:
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
The dropdown QIcon are horizontally centered but vertically they are not and although, I try to remove the text using following line but unless the string is "", the text is still somewhere.
option.features &= ~QStyleOptionViewItem.ViewItemFeature.HasDisplay
Same goes for the selected line where, I can't figure how to not have text (besides "") and have the QIcon centered instead of the text.
class MyComboBox(QComboBox):
def __init__(self, *args, **kwargs):
super().__init__()
self.setEditable(True)
self.lineEdit().setReadOnly(True)
self.lineEdit().setAlignment(Qt.AlignmentFlag.AlignCenter)
There are two problems in play:
The attempt to alter the editable behavior is also pointless: an editable QComboBox embeds a QLineEdit (which can only show text), and the icon is always drawn by the combo box anyway, which eventually alters the line edit geometry in order to fit the icon; trying to set the line edit alignment is useless, because it has absolutely no impact on the icon position.
Popup display
Since most functions of Qt widgets (including delegates) always rely on the current style, this means that the only way to ensure that the icon is always centered is to override the
paint()function of the delegate.Note that we should always try to follow the default behavior: the default delegate of QComboBox is QItemDelegate, not QStyledItemDelegate.
QItemDelegate has separated functions for each element of an item; since we only want to show the icon, we just need to call
drawBackground()(which also draws the selected state of the highlighted item) and then paint the icon.If you still want to use the capabilities of QStyledItemDelegate (including style sheets), then we can do a similar approach by calling the related function of the style, which is
drawPrimitive()along withPE_PanelItemViewItem.Actual combo box display
As said above, the delegate only has effect on the popup (the view), not on the combo box.
In order to change such display, we need to follow the default behavior of QComboBox by overriding
paintEvent()and "port" the original C++ code into Python.The default
paintEvent()implementation does the following:-1;Since we obviously only want to show the icon whenever the current index is valid, we can skip all that
if currentIndex() < 0, otherwise, we will only do the first step, and eventually draw the icon.Then, proper centering of the icon is achieved by calling the current style
subControlRect()function with theSC_ComboBoxEditFieldsubcontrol, which ensures that we get the proper rectangle in which the "label" would be shown, excluding the arrow button of the combo box.Note that this means that we are possibly breaking the default behavior in case any future Qt release alters the default implementation, possibly due to bug reports/fixes, changes in QComboBox painting or even QStyle implementations.