简体   繁体   English

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

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

So I have a table with 1 row and multiple columns and I use the custom delegate function for the image thumbnail.所以我有一个 1 行多列的表格,我使用自定义委托 function 作为图像缩略图。 I also included the set background colour function when I click it.当我单击它时,我还包括了设置的背景颜色 function。 However, it doesn't change the colour of the item background.但是,它不会更改项目背景的颜色。 I have to use the custom delegate function so that my icon image is sticking with each other from different cell without any additional spaces between.我必须使用自定义委托 function 以便我的图标图像从不同的单元格相互粘贴,而没有任何额外的空格。

my code is as below我的代码如下

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

This is the image with the gap between icons and it form not a perfect looking arrow thats why I used the custom delegate function to stick the icon together and let it look like a perfect arrow这是图标之间有间隙的图像,它形成的不是一个完美的箭头,这就是为什么我使用自定义委托 function 将图标粘在一起,让它看起来像一个完美的箭头

第一张图片

This is the image that has the perfect look of the arrow as it doesnt has any gap between the cells by using the delegate function.这是具有完美箭头外观的图像,因为使用委托 function 在单元格之间没有任何间隙。 However, it doesnt allow me to change the background colour for the cell.但是,它不允许我更改单元格的背景颜色。

第二张图片

This is the image of the arrowHead这是箭头的图像

箭头

This is the image of the arrowBody这是箭头体的图像

箭头体

As with any overridden function, if the base implementation is not called, the default behavior is ignored.与任何重写的 function 一样,如果未调用基本实现,则忽略默认行为。

paint() is the function responsible of drawing any aspect of the item, including its background. paint()是 function 负责绘制项目的任何方面,包括其背景。 Since in your override you are just drawing the pixmap, everything else is missing, so the solution would be to call the base implementation and then draw the pixmap.由于在您的覆盖中您只是在绘制像素图,因此缺少其他所有内容,因此解决方案是调用基本实现然后绘制像素图。

In this specific case, though, this wouldn't be the right choice, as the base implementation already draws the item's decoration, and in certain conditions (assuming that the pixmap has an alpha channel), it will probably result in having the images overlapping.但是,在这种特定情况下,这不是正确的选择,因为基本实现已经绘制了项目的装饰,并且在某些条件下(假设像素图具有 alpha 通道),它可能会导致图像重叠.

There are three possible solutions.有三种可能的解决方案。

Use QStyle functions ignoring the icon使用忽略图标的 QStyle 函数

In this case, we do what the base implementation would, which is calling the style drawControl function with the CE_ItemViewItem , but we modify the option before that, removing anything related to the icon:在这种情况下,我们执行基本实现的操作,即使用 CE_ItemViewItem 调用样式drawControl CE_ItemViewItem ,但我们在此之前修改选项,删除与图标相关的任何内容:

    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)
        # ...

Update the icon information in the delegate更新委托中的图标信息

A QStyledItemDelegate always callsinitStyleOption at the beginning of most its functions, including paint and sizeHint . QStyledItemDelegate 总是在其大多数函数的开头调用initStyleOption ,包括paintsizeHint By overriding that function, we can change some aspects of the drawing, including the icon alignment and positioning.通过覆盖 function,我们可以更改绘图的某些方面,包括图标 alignment 和定位。

In this specific case, knowing that we have only two types of icons, and they must be right or left aligned depending on the image, it's enough to update the proper decoration size (based on the optimal size of the icon), and then the alignment based on the column.在这种特定情况下,知道我们只有两种类型的图标,并且它们必须根据图像右对齐或左对齐,因此更新适当的装饰尺寸(基于图标的最佳尺寸)就足够了,然后alignment 基于列。

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

Note: this approach only works as long as the images all have the same ratio, and the row height is not manually changed.注意:此方法仅适用于图像都具有相同比例且未手动更改行高的情况。

Use a custom role for the image为图像使用自定义角色

Qt uses roles to store and retrieve various aspects of each index (font, background, etc.), and user roles can be defined to store custom data. Qt 使用角色来存储和检索每个索引的各个方面(字体、背景等),并且可以定义用户角色来存储自定义数据。

setIcon() actually sets a QIcon in the item using the DecorationRole , and the delegate uses it to draw it. setIcon()实际上使用DecorationRole在项目中设置一个QIcon,并且委托使用它来绘制它。 If the data returned for that role is empty/invalid, no icon is drawn.如果为该角色返回的数据为空/无效,则不会绘制任何图标。

If we don't use setIcon() but store the icon in the item with a custom role instead, the default drawing functions will obviously not draw any decoration, but we can still access it from the delegate.如果我们不使用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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM