单击 qTableWidget pyqt5 中的特定单元格后,特定单元格的背景颜色不会改变

Background color of the particular cell is not changing after clicking specific cells in qTableWidget pyqt5

所以我有一个包含 1 行和多列的 table,并且我使用图像缩略图的自定义委托函数。单击它时,我还包含了设置背景颜色功能。但是,它不会更改项目背景的颜色。我必须使用自定义委托功能,以便我的图标图像与来自不同单元格的图标图像相互粘在一起,并且之间没有任何额外的空格。

我的代码如下

import random

from PyQt5 import QtCore, QtGui, QtWidgets


imagePath2 = "arrowREDHead.png"
imagePath = "arrowREDBody.png"
class IconDelegate(QtWidgets.QStyledItemDelegate):
    def paint(self, painter, option, index):
        icon = index.data(QtCore.Qt.DecorationRole)
        mode = QtGui.QIcon.Normal
        if not (option.state & QtWidgets.QStyle.State_Enabled):
            mode = QtGui.QIcon.Disabled
        elif option.state & QtWidgets.QStyle.State_Selected:
            mode = QtGui.QIcon.Selected
        state = (
            QtGui.QIcon.On
            if option.state & QtWidgets.QStyle.State_Open
            else QtGui.QIcon.Off
        )
        pixmap = icon.pixmap(option.rect.size(), mode, state)
        painter.drawPixmap(option.rect, pixmap)

    def sizeHint(self, option, index):
        return QtCore.QSize(20, 20)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        table = QtWidgets.QTableWidget(1, 10)
        delegate = IconDelegate(table)
        table.setItemDelegate(delegate)

        self.setCentralWidget(table)

        for j in range(table.columnCount()):

            if(j % 2 == 0):

                pixmap = QtGui.QPixmap(imagePath)
                icon = QtGui.QIcon(pixmap)

                it = QtWidgets.QTableWidgetItem()
                it.setIcon(icon)
                table.setItem(0, j, it)
            
            else:
                pixmap = QtGui.QPixmap(imagePath2)
                icon = QtGui.QIcon(pixmap)
                it = QtWidgets.QTableWidgetItem()
                it.setIcon(icon)
                table.setItem(0, j, it)
                table.item(0, 1).setBackground(QtGui.QColor(100,100,150))

                
        table.resizeRowsToContents()
        table.resizeColumnsToContents()
        table.setShowGrid(False)
        table.itemClicked.connect(self.sequenceClicked)

    def sequenceClicked(self, item): 
        self.itemClicked = item
        print("item", item)
        item.setBackground(QtGui.QColor(255, 215, 0))


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

这是图标之间有间隙的图像,它形成的箭头看起来并不完美,这就是为什么我使用自定义委托功能将图标粘在一起并让它看起来像一个完美的箭头

这是具有完美箭头外观的图像,因为它使用委托函数在单元格之间没有任何间隙。但是,它不允许我更改单元格的背景颜色。

这是箭头的图像

这是箭体的图像

与任何重写函数一样,如果未调用基本实现,默认行为将被忽略。

paint() 是负责绘制项目任何方面的功能,包括其背景。由于在您的覆盖中您只是绘制像素图,其他所有内容都丢失了,因此解决方案是调用基本实现,然后然后绘制像素图。

但是,在这种特定情况下,这不是正确的选择,因为基础实现已经绘制了项目的装饰,并且在某些情况下(假设像素图具有 alpha 通道),它可能会导致让图像重叠。

存在三种可能的解决方案。

使用 QStyle 函数忽略图标

在这种情况下,我们执行基本实现会执行的操作,即使用 CE_ItemViewItem 调用样式 drawControl 函数,但我们在此之前修改选项,删除与图标相关的所有内容:

    def paint(self, painter, option, index):
        # create a new option (as the existing one should not be modified)
        # and initialize it for the current index
        option = QtWidgets.QStyleOptionViewItem(option)
        self.initStyleOption(option, index)

        # remove anything related to the icon
        option.features &= ~option.HasDecoration
        option.decorationSize = QtCore.QSize()
        option.icon = QtGui.QIcon()

        if option.widget:
            style = option.widget.style()
        else:
            style = QtWidgets.QApplication.style()
        # call the drawing function for view items
        style.drawControl(style.CE_ItemViewItem, option, painter, option.widget)

        # proceed with the custom drawing of the pixmap
        icon = index.data(QtCore.Qt.DecorationRole)
        # ...

更新委托中的图标信息

QStyledItemDelegate 总是在其大多数函数的开头调用initStyleOption,包括paintsizeHint。通过覆盖该函数,我们可以更改绘图的某些方面,包括图标对齐和定位。

在这种特定情况下,知道我们只有两种类型的图标,并且它们必须根据图像右对齐或左对齐,更新适当的装饰尺寸就足够了(基于图标的最佳尺寸),然后是基于列的对齐方式。

class IconDelegate(QtWidgets.QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super().initStyleOption(option, index)
        option.decorationSize = option.icon.actualSize(option.rect.size())
        if index.column() & 1:
            option.decorationPosition = option.Left
            option.decorationAlignment = QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter
        else:
            option.decorationPosition = option.Right
            option.decorationAlignment = QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter

注意:此方法只适用于所有图像具有相同比例且行高未手动更改的情况。

为图像使用自定义角色

Qt 使用 roles 来存储和检索每个索引的各个方面(字体、背景等),并且 user 角色可以被定义为存储自定义数据。

setIcon() 实际上在使用 DecorationRole 的项目中设置了一个 QIcon,委托使用它来绘制它。如果为该角色返回的数据是 empty/invalid,则不会绘制任何图标。

如果我们不使用 setIcon() 而是将图标存储在具有自定义角色的项目中,默认绘图函数显然不会绘制任何装饰,但我们仍然可以从委托中访问它。

# we usually define roles right after the imports, as they're constants
ImageRole = QtCore.Qt.UserRole + 1

class IconDelegate(QtWidgets.QStyledItemDelegate):
    def paint(self, painter, option, index):
        super().paint(painter, option, index)
        icon = index.data(ImageRole)
        # ...

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        # ...
                it = QtWidgets.QTableWidgetItem()
                it.setData(ImageRole, pixmap)