How Do I Use a QStyledItemDelegate to Paint Only the Background, Without Covering the Text?

时间秒杀一切 提交于 2019-12-24 02:52:16

问题


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:


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.



来源:https://stackoverflow.com/questions/10924175/how-do-i-use-a-qstyleditemdelegate-to-paint-only-the-background-without-coverin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!