How Do I Use a QStyledItemDelegate to Paint Only t

2019-07-23 17:26发布

How should I change just the background color of my tree item using a delegate?

I haven't been able to figure out how to do this without painting on top of the text. In other words, when I use the code below, the color is drawn on top of the text. The text is under the background...

def paint(self, painter, option, index):
    MyDelegate.paint(self, painter, option, index)

    painter.save()

    # set background color
    painter.setPen(QPen(Qt.NoPen))
    if self.item.is_set and option.state & QStyle.State_Selected:
        painter.setBrush(QBrush(QtGui.QColor(100, 200, 0, 200)))
    else:
        painter.setBrush(QBrush(Qt.NoBrush))

    painter.drawRect(option.rect)

    painter.restore()

I want to inherit as much as I can.

...

Regarding the comments, I tried to apply the answer " Set color to a QTableView row " ... but I can't seem to get it to work in Python. I'm not sure how the optionV4.backgroundBrush() as any effect. Is the option used by reference so that this change is supposed to be used without a return value?

(These are used without overriding paint)

def initStyleOption(self, option, index):
    print("initStyleOption...")
    MyBaseDelegate.initStyleOption(self, option, index)

    optionV4 = QtGui.QStyleOptionViewItemV4(option)
    optionV4.backgroundBrush = QBrush(QtGui.QColor(100, 200, 100, 200))

I also tried this:

def initStyleOption(self, option, index):
    MyBaseDelegate.initStyleOption(self, option, index)
    option.backgroundBrush = QBrush(QtGui.QColor(100, 200, 100, 200))

...and this:

def initStyleOption(self, option, index):
    option.backgroundBrush = QBrush(QtGui.QColor(100, 200, 100, 200))
    MyBaseDelegate.initStyleOption(self, option, index)

And according to the docs, this should not return anything, so I assume option IS passed by reference, which makes sense.

I found another example, which works (though I haven't tried to understand it)

def paint(self, painter, option, index):
    option = QtGui.QStyleOptionViewItemV4(option)  # Needed for "widget"
    self.initStyleOption(option, index)  # <--- I did not override this

    option.backgroundBrush = QBrush(QtGui.QColor(100, 200, 100, 200))
    option.widget.style().drawControl(QStyle.CE_ItemViewItem, option, painter)

    ColumnBaseDelegate.paint(self, painter, option, index)

...but the background color is painted over by the selection color. Since I am trying to change the background color when the item is selected, this is a problem. Is there a different brush or method for working with selection colors? I need certain types of items to be a different color.

I tried the answer which demonstrates inverting the selection state before drawing my own background, but it painted an empty background for me for some reason. Putting it in paint worked though...

def paint(self, painter, option, index):

    if option.state & QStyle.State_Selected:
        option.state &= ~QStyle.State_Selected  # Invert state

        # This "cast", which is needed to get option.widget, seems to be
        #   needed for backgroundBrush as well.
        option = QtGui.QStyleOptionViewItemV4(option)  # Cast for widget
        option.backgroundBrush = QBrush(QColor(100, 200, 100, 200))

        option.widget.style().drawControl(QStyle.CE_ItemViewItem, option, painter)

    super(MyDelegate, self).paint(painter, option, index)

I don't understand why, so I won't add it to an answer at this point.

1条回答
我欲成王,谁敢阻挡
2楼-- · 2019-07-23 17:36

To override background color for selected items, you can disable the selected flag in initStyleOption. For example:

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class SelectionColorDelegate(QStyledItemDelegate):
        def __init__(self, parent):
        super(SelectionColorDelegate, self).__init__(parent)

    def initStyleOption(self, option, index):
        # let the base class initStyleOption fill option with the default values
        super(SelectionColorDelegate,self).initStyleOption(option, index)
        # override what you need to change in option
        if option.state & QStyle.State_Selected:
            option.state &= ~ QStyle.State_Selected
            option.backgroundBrush = QBrush(QColor(100, 200, 100, 200))

if __name__ == "__main__":
    app = QApplication(sys.argv)    
    treeWidget = QTreeWidget()
    treeWidget.setColumnCount(2)
    for i in range(5):
        item = QTreeWidgetItem(["Item %d"%i, "data" ])
        treeWidget.addTopLevelItem(item)
        for j in range(3):
            subItem = QTreeWidgetItem(["SubItem %d, %d"%(i,j), "subdata"])
            item.addChild(subItem)
        treeWidget.expandItem(item)

    treeWidget.setItemDelegate(SelectionColorDelegate(treeWidget))    
    treeWidget.show()   

    app.exec_()
    sys.exit()

Apparently, the background is also painted by QTreeView.drawRow before the delegate paint function is called, so for indented items, a part of that background keeps the default color.

查看更多
登录 后发表回答