Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

how to repaint a QComboBox ?



  • I'm trying to repaint a QCombobox, so I can control the text/arrow placement, along with multicolored text when an index changed from the 0.
    combobox_delegate.jpg

    I tried to subclass a QStyledItemDelegate and assign the custom delegate to a combobox. The custom delegate's paint() seems to only affect the popup listview in the combobox. What do I need to modify in order to repaint the Comboox itself? Is it an Editor? The documentation's "Detailed Description" on the QComboClass didn't help me understand how the combobox is made up, other than it has it's own listview for it's items.

    from PySide2 import QtGui, QtCore, QtWidgets
    
    class CustomComboDelegate(QtWidgets.QStyledItemDelegate):
        def __init__(self, parent=None):
            super(CustomComboDelegate, self).__init__(parent)
    
            self.text_default = QtGui.QColor(199, 199, 199)
            self.text_hover = QtGui.QColor(255, 255, 255)
            self.cell_default = QtGui.QColor(67, 67, 67)
            self.cell_hover = QtGui.QColor(43, 43, 43)
    
        def paint(self, painter, option, index):
            painter.save()
            value = index.data(QtCore.Qt.DisplayRole)
            painter.fillRect(option.rect, self.cell_default)
            painter.setPen(self.text_default)
            painter.drawText(option.rect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, value)
            if option.state & QtWidgets.QStyle.State_MouseOver:
                painter.fillRect(option.rect, self.cell_hover)
                painter.setPen(self.text_default)
                painter.drawText(option.rect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, value)
            painter.restore()
    
    
    app = QtWidgets.QApplication([])
    
    combo =  QtWidgets.QComboBox()
    combo.setItemDelegate(CustomComboDelegate(combo))
    combo.addItems(['one', 'two', 'three'])
    combo.show()
    app.exec_()
    

    Cheers


  • Lifetime Qt Champion

    Hi,

    Depending on your goal, maybe using a style sheet would do the job.

    Otherwise, you will likely have to take a look at QComboBox sources to learn how the painting is done.



  • Cheers,
    Yeah I don't think stylesheets will get me where I would like to go. So it tried to subclass a combobox and override the paintEvent. I had a look at the source cpp and ~think i got it... :P

    seems to work atm, not sure how safe this approach is though. I still need to add the logic to repaint a changed selection:

    from PySide2 import QtGui, QtCore, QtWidgets
    
    
    class CustomComboDelegate(QtWidgets.QStyledItemDelegate):
        def __init__(self, parent=None):
            super(CustomComboDelegate, self).__init__(parent)
            self.text_default = QtGui.QColor(199, 199, 199)
            self.text_hover = QtGui.QColor(255, 255, 255)
            self.cell_default = QtGui.QColor(67, 67, 67)
            self.cell_hover = QtGui.QColor(43, 43, 43)
    
        def paint(self, painter, option, index):
            painter.save()
            value = index.data(QtCore.Qt.DisplayRole)
            painter.fillRect(option.rect, self.cell_default)
            painter.setPen(self.text_default)
            painter.drawText(option.rect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, value)
            if option.state & QtWidgets.QStyle.State_MouseOver:
                painter.fillRect(option.rect, self.cell_hover)
                painter.setPen(self.text_default)
                painter.drawText(option.rect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, value)
            painter.restore()
    
    
    class CustomQComboBox(QtWidgets.QComboBox):
        def __init__(self, parent=None):
            super(CustomQComboBox, self).__init__(parent)
            self.__hovered = False
    
        def paintEvent(self, event):
            data = self.itemData(self.currentIndex(), QtCore.Qt.DisplayRole)
            if data:
                p = QtWidgets.QStylePainter(self)
                p.setPen(self.palette().color(QtGui.QPalette.Text))
                opt = QtWidgets.QStyleOptionComboBox()
                self.initStyleOption(opt)
                p.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, opt)
                painter = QtGui.QPainter(self)
                painter.save()
                pen = QtGui.QPen()
                pen.setColor(QtGui.QColor(199, 199, 199))
                if self.__hovered :
                    painter.fillRect(opt.rect, QtGui.QColor(43, 43, 43))
                    painter.setPen(QtCore.Qt.yellow)
                    painter.drawText(opt.rect.center(), data)
                    painter.setPen(QtCore.Qt.red)
                    painter.drawText(
                        opt.rect.center().x() + QtGui.QFontMetrics(painter.font()).size(QtCore.Qt.TextSingleLine,
                                                                                        data).width(),
                        opt.rect.center().y(), "(+)")
                else:
                    painter.fillRect(opt.rect, QtGui.QColor(43, 43, 43))
                    painter.setPen(QtCore.Qt.yellow)
                    painter.drawText(opt.rect.center(), data)
                    painter.setPen(QtCore.Qt.red)
                painter.restore()
    
            else:
                super(CustomQComboBox, self).paintEvent(event)
    
        def mouseMoveEvent(self, event):
            if self.rect().contains(event.pos()):
                self.__hovered = True
            super(CustomQComboBox, self).mouseMoveEvent(event)
    
        def leaveEvent(self, event):
            self.__hovered = False
            super(CustomQComboBox, self).leaveEvent(event)
    
    app = QtWidgets.QApplication([])
    
    combo = CustomQComboBox()
    combo.setMouseTracking(True)
    combo.setItemDelegate(CustomComboDelegate(combo))
    combo.addItems(['one', 'two', 'three'])
    combo.show()
    app.exec_()
    

    Is it worth to investigate setting up signals on the combobox to test the hover state and indexChanged or to manage this in the events? This is an area I'm not sure what methodology has the best performance/less maintenance


Log in to reply