如何使用QStyledItemDelegate仅绘制背景而不覆盖文本?

3

如何使用代理只更改树形项的背景颜色?

我没有找到不在文本上绘制的情况下如何实现这一点的方法。换句话说,当我使用下面的代码时,颜色会绘制在文本上方。文本位于背景下方...

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()

我想尽可能多地继承。
...
关于评论,我尝试应用答案“设置QTableView行的颜色”,但是我似乎无法在Python中使其工作。 我不确定optionV4.backgroundBrush()如何起作用。 这个选项是否通过引用使用,因此该更改应在没有返回值的情况下使用?
(这些是在没有覆盖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))

我也尝试了这个方法:
def initStyleOption(self, option, index):
    MyBaseDelegate.initStyleOption(self, option, index)
    option.backgroundBrush = QBrush(QtGui.QColor(100, 200, 100, 200))

...还有这个:

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

根据文档,这应该不返回任何内容,因此我认为选项通过引用传递,这是有道理的。

我找到了另一个示例,它可以正常工作(尽管我还没有尝试理解它)

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)

...但是选定颜色会覆盖背景色。由于我尝试在项目被选中时更改背景色,这是一个问题。是否有不同的画刷或方法可以处理选择颜色?我需要某些类型的项目显示不同的颜色。

我尝试了答案,演示了绘制自己的背景之前反转选择状态,但出于某种原因它为我绘制了一个空背景。把它放在Paint中起作用……

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)

我不明白为什么,所以暂时不会将其添加到答案中。

也许这个问题https://dev59.com/kWPVa4cB1Zd3GeqP-e8c#10630476会对你有所帮助。请查看我的回答。尽管该问题是关于设置进度条的样式,但同样可以用于树形视图。 - Ammar
可能是 Set color to a QTableView row的重复问题。 - alexisdm
@alexisdm:我在Python中尝试执行某些操作遇到了问题。请查看我的帖子。 - Rafe
@Ammar:我没有看到任何委托被使用。你是说应用样式是正确的方法吗?样式可以直接应用于索引吗?你能否发布一个普通项目背景的简单通用示例? - Rafe
赋值在C++中是类型转换,但在Python中是复制。在这里不需要进行转换,只需使用option.backgroundBrush = QBrush(...)即可。是的,option参数就是返回值。 - alexisdm
@alexisdm:我已经在上面的问题中添加了我尝试过的内容。如果您有一个可行的示例,请发布它。该函数正在被调用(我的打印行有效),但似乎没有任何效果。我需要在另一个函数中添加一些东西吗?我还需要paint()函数吗(我已将其注释掉)?再次感谢您的帮助。我希望我已经接近成功了。 - Rafe
1个回答

4

要覆盖所选项目的背景颜色,您可以在 initStyleOption 中禁用所选标志。例如:

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()

显然,在代理 paint 函数被调用前,QTreeView.drawRow 也会绘制背景,因此对于缩进的项来说,部分背景仍保持默认颜色。

请查看我答案的编辑。由于某些原因,这对我没有起作用。不过它仍然非常有帮助!我将不得不查看树的“drawRow”并在那里模拟它。保留选择背景的第一部分以原始颜色绘制不够美观。 - Rafe
仍有更多事情正在发生。我尝试覆盖drawRow,但它为整个行绘制了背景,即使它只接收index.column() 0。由于某种原因,默认行为不会强制在其他列上着色背景,这些列由委托处理,该委托根本不绘制背景(通常看起来只选择了列0,视觉上)。我很想看到drawRow和委托的paint的完整python实现。这将解决许多问题。 - Rafe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接