O. Shehab
O. Shehab

Reputation: 81

Styling the items of a QComboBox menu individually PyQt5

I was trying to style the items of the QComboBox menu individually. In the Qt Style Sheets Examples it styles the menu through the QAbstractItemView

QComboBox QAbstractItemView {
    border: 2px solid darkgray;
    selection-background-color: lightgray;
}

which provides a general control over the menu but no control over the individual items except a slight control over the selected item. Another solution was to use QAbstractItemView::item which didn't work for me.

Upvotes: 1

Views: 1289

Answers (1)

eyllanesc
eyllanesc

Reputation: 243965

It seems that you have a XY problem since as you point out your objective is "paint the dropdown menu"(which is very generic) but you ask for the error of a possible solution (of another question) that will not necessarily serve you.

Considering the above, I will explain the cause of the error and a possible solution (obviously making many assumptions).

The main error is that this code is for PyQt4 and you are probably using PyQt5, in PyQt4 the data() method returns a QVariant that must be converted to a python object, so you use isValid() and toPyObject() but in pyqt5 it is no longer necessary. Another error is that the UserRole value will be None since you have not assigned any value when creating the item (this can be caused by the omission in the other question).

Considering the above, a possible solution (compatible for PyQt4 and PyQt5) is:

class LineStyleDelegate(QItemDelegate):
    def paint(self, painter, option, index):
        data = index.data(Qt.UserRole)
        if hasattr(data, "toPyObject"):
            data = data.toPyObject()
        if data is not None:
            painter.save()
            rect = option.rect
            rect.adjust(+5, 0, -5, 0)
            pen = QPen()
            pen.setColor(Qt.black)
            pen.setWidth(3)
            pen.setStyle(data)
            painter.setPen(pen)
            middle = (rect.bottom() + rect.top()) / 2
            painter.drawLine(rect.left(), middle, rect.right(), middle)
            painter.restore()
        else:
            QItemDelegate.paint(self, painter, option, index)
        self.searchEdit = QComboBox(sef.searchContent)
        for text, style in (
            ("Item 1", Qt.SolidLine),
            ("Item 2", Qt.DotLine),
            ("Item 3", Qt.DashDotDotLine),
        ):
            self.searchEdit.addItem(text, style)
        self.delegate = LineStyleDelegate(self.searchEdit)
        self.searchEdit.setItemDelegate(self.delegate)
        self.searchEdit.setMinimumWidth(500)
        self.searchEdit.setEditable(True)

UPDATE:

With the modification of the question it is verified that the OP had an XY problem. To modify the painting of an item of the QComboBox a delegate must be used: QItemDelegate or QStyledItemDelegate. I prefer to use the second one since it uses the QStyle, that is, the design will respect the style of the GUI. To set the color of each item, use the backgroundBrush attribute of the QStyleOptionViewItem, and for the border painting then the paint() method must be overridden:

import random

from PyQt5 import QtCore, QtGui, QtWidgets


class CustomStyleDelegate(QtWidgets.QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super(CustomStyleDelegate, self).initStyleOption(option, index)
        random_color = QtGui.QColor(*random.sample(range(255), 3))
        option.backgroundBrush = random_color

    def paint(self, painter, option, index):
        super(CustomStyleDelegate, self).paint(painter, option, index)
        margins = 2
        border_color = QtGui.QColor(*random.sample(range(255), 3))
        painter.save()
        pen = QtGui.QPen()
        pen.setColor(border_color)
        pen.setWidth(margins)
        painter.setPen(pen)
        r = QtCore.QRect(option.rect).adjusted(0, 0, -margins, -margins)
        painter.drawRect(r)
        painter.restore()


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = QtWidgets.QComboBox()
    w.addItems(["Item 1", "Item 2", "Item 3"])
    delegate = CustomStyleDelegate(w)
    w.setItemDelegate(delegate)
    w.show()
    sys.exit(app.exec_())

Upvotes: 2

Related Questions