为 QListWidget 中的特定项目设置不同的颜色

2022-01-12 00:00:00 python pyqt pyqt5

问题描述

我有一个 QListWidget,我想为列表的每个项目添加边框底部并为项目设置背景颜色,我想为特定项目设置不同的背景颜色.所以我使用 my_list.setStyleSheet("QListWidget::item {border-bottom: 1px solid red; background-color: blue;}") 并将背景颜色设置为我使用的特定项目 item.setBackground(QtGui.QColor("#E3E3E3"))

问题是我设置不同颜色的特定项目没有得到这个颜色.

解决方案

您不能在小部件上使用样式表和样式设置的组合——样式表将覆盖单个项目的任何设置.例如,以下代码在每个项目上使用 setBackground 来更改背景颜色.

从 PyQt5.QtGui 导入 *从 PyQt5.QtWidgets 导入 *颜色= ['#7fc97f','#beaed4','#fdc086','#ffff99','#386cb0','#f0027f','#bf5b17','#666666']类主窗口(QMainWindow):def __init__(self, *args, **kwargs):super(MainWindow, self).__init__(*args, **kwargs)w = QListWidget()对于范围内的 n (8):i = QListWidgetItem('%s' % n)i.setBackground(QColor(颜色[n]))w.addItem(i)self.setCentralWidget(w)自我展示()应用程序 = QApplication([])w = 主窗口()app.exec_()

结果输出:

但是,如果我们在结果中添加样式表行(第二个只有底部边框):

很遗憾,无法设置项目的边框和颜色.但是,您可以做的就是插入一个自定义小部件作为列表项,或者

使用委托的方法是定义一个 paint 方法,该方法接受一个 QPainter 对象(您使用它来进行实际绘图),一个 option 参数包含项目的矩形(相对于父窗口小部件)和一个 index,您可以通过它检索项目数据.然后,您使用 QPainter 上的方法来绘制您的项目.

在上面的示例中,我们使用它来传递项目标签(位置 Qt.DisplayRole)和十六进制颜色(位置 Qt.DisplayRole+1).ItemDisplayRole 的名称文档列出了其他定义的数据角色",但在大多数情况下,您可以忽略这些.

I have a QListWidget and I want to add border bottom to each item of the list and set a background color to items and I want to set to specific items a different background color. So I used my_list.setStyleSheet("QListWidget::item { border-bottom: 1px solid red; background-color: blue;}") and to set background color to specific items I used item.setBackground(QtGui.QColor("#E3E3E3"))

The problem is the specif items that I set a different color don't get this color.

解决方案

You can't use a combination of stylesheets and style settings on a widget — the stylesheet will override any settings on individual items. For example, the following code uses setBackground on each item to change the background colour.

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

colors = [ '#7fc97f', '#beaed4', '#fdc086', '#ffff99', '#386cb0', '#f0027f', '#bf5b17', '#666666']

class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        w = QListWidget()
        for n in range(8):
            i = QListWidgetItem('%s' % n)
            i.setBackground( QColor(colors[n]) )
            w.addItem(i)

        self.setCentralWidget(w)

        self.show()


app = QApplication([])
w = MainWindow()
app.exec_()

The resulting output:

However, if we add the stylesheet line in the result is (and second with only the bottom border):

Unfortunately, there is no way to set the border and the colour for the items. However, what you can do is either insert a custom widget as the list item, or use an item delegate to draw the item. This gives you complete control over appearance, however you have to handle drawing yourself. Below is an example of doing this with a custom delegate:

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

colors = [ '#7fc97f', '#beaed4', '#fdc086', '#ffff99', '#386cb0', '#f0027f', '#bf5b17', '#666666']


class MyDelegate(QItemDelegate):
    def __init__(self, parent=None, *args):
        QItemDelegate.__init__(self, parent, *args)

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

        # set background color
        painter.setPen(QPen(Qt.NoPen))
        if option.state & QStyle.State_Selected:
            # If the item is selected, always draw background red
            painter.setBrush(QBrush(Qt.red))
        else:
            c = index.data(Qt.DisplayRole+1) # Get the color
            painter.setBrush(QBrush(QColor(c)))

        # Draw the background rectangle            
        painter.drawRect(option.rect)

        # Draw the bottom border
        # option.rect is the shape of the item; top left bottom right
        # e.g. 0, 0, 256, 16 in the parent listwidget
        painter.setPen(QPen(Qt.red))        
        painter.drawLine(option.rect.bottomLeft(), option.rect.bottomRight())

        # Draw the text
        painter.setPen(QPen(Qt.black))
        text = index.data(Qt.DisplayRole)
        # Adjust the rect (to pad)
        option.rect.setLeft(5)
        option.rect.setRight(option.rect.right()-5)
        painter.drawText(option.rect, Qt.AlignLeft, text)

        painter.restore()


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        de = MyDelegate(self)
        w = QListWidget()
        w.setItemDelegate(de)
        for n in range(8):
            s = '%s' % n
            i = QListWidgetItem()
            i.setData(Qt.DisplayRole, s) # The label
            i.setData(Qt.DisplayRole+1, colors[n]) # The color
            w.addItem(i)

        self.setCentralWidget(w)

        self.show()


app = QApplication([])
w = MainWindow()
app.exec_()

Which gives the following output:

The approach with delegates is to define a paint method, which accepts a QPainter object (with which you do the actual drawing), an option parameter containing the rectangle of the item (relative to the parent widget) and an index through which you can retrieve the item data. You then use the methods on the QPainter to draw your item.

In the example above we use this to pass in both the item label (at position Qt.DisplayRole) and the color in hex (at position Qt.DisplayRole+1). The name docs for ItemDisplayRole list the other defined data 'roles', but for most purposes you can ignore these.

相关文章