Search code examples
pythonpython-2.7pyqtpyqt4qtablewidget

Selection highlight in PyQt4 QTableWidget fill selected cell's background with full block color


I am working on a small PyQt4 task manager. A similar question is ask in here Change QTableWidget default selection color, and make it semi transparent . From this post, I try to setStyleSheet for selection background color opacity, but the highlight is still override the cell background color. Is there someone can help me show how to change this to border color?

Image below is my current result

enter image description here

This is what I willing to achieve, as you see, the highlight selectionis just overlay the background color, but not override it.

enter image description here

At the end, hopefully my question is clear enough for everyone, if found any unclear or mistake, please kindly let me know, I will fix as fast as possible! Thanks!


Solution

  • One way to change the colors is to use a delegate.

    For this we must get the current background color, the task of getting the background color is tedious since a QTableWidget has its own color as its background, it also has the colors that you add to QTableWidgets and other types of elements so my answer currently has limited support but the idea is scalable.

    The color to be displayed as the background of the selected element is an average of the background color and a color chosen properly, in this case we choose the color #cbedff

    I have implemented all of the above in the following class:

    class TableWidget(QTableWidget):
        def __init__(self, *args, **kwargs):
            QTableWidget.__init__(self, *args, **kwargs)
    
            class StyleDelegateForQTableWidget(QStyledItemDelegate):
                color_default = QColor("#aaedff")
    
                def paint(self, painter, option, index):
                    if option.state & QStyle.State_Selected:
                        option.palette.setColor(QPalette.HighlightedText, Qt.black)
                        color = self.combineColors(self.color_default, self.background(option, index))
                        option.palette.setColor(QPalette.Highlight, color)
                    QStyledItemDelegate.paint(self, painter, option, index)
    
                def background(self, option, index):
                    item = self.parent().itemFromIndex(index)
                    if item:
                        if item.background() != QBrush():
                            return item.background().color()
                    if self.parent().alternatingRowColors():
                        if index.row() % 2 == 1:
                            return option.palette.color(QPalette.AlternateBase)
                    return option.palette.color(QPalette.Base)
    
                @staticmethod
                def combineColors(c1, c2):
                    c3 = QColor()
                    c3.setRed((c1.red() + c2.red()) / 2)
                    c3.setGreen((c1.green() + c2.green()) / 2)
                    c3.setBlue((c1.blue() + c2.blue()) / 2)
    
                    return c3
    
            self.setItemDelegate(StyleDelegateForQTableWidget(self))
    

    Example:

    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = TableWidget()
        w.setColumnCount(10)
        w.setRowCount(10)
        for i in range(w.rowCount()):
            for j in range(w.columnCount()):
                w.setItem(i, j, QTableWidgetItem("{}".format(i * j)))
                if i < 8 and j < 8:
                    color = QColor(qrand() % 256, qrand() % 256, qrand() % 256)
                    w.item(i, j).setBackground(color)
        w.show()
        sys.exit(app.exec_())
    

    Deselected:

    enter image description here

    Selected:

    enter image description here