2 years ago

#57200

test-img

Tuhin Mitra

How to display QTableView Cell background image along with QTableView stylesheet

I want to set stylesheet for QTableView that shows round border along with background image for each cell. This works fine with the following stylesheet:

QTableView{
    gridline-color: rgba(0,0,0,0);
}
QTableView::item{
    color: white;
    background-image: url(cell_bg.png); /*cell bg image*/
    padding-left: 5px; /*cell spacing*/
    border: 2px solid transparent; /*transparent cell borders*/
    border-radius: 10px;
}

But, in the QAbstractTableModel for the TableView I am using Qt.BackgroundColorRole to set cell colors based on some logic. When I use the above stylesheet, custom cell colors are not shown. QTableView without background-image stylesheet property looks like this:

QTableView without background-image stylesheet property

QTableView with the stylesheet looks like this:

enter image description here

Is there anyway I can set cell background image using stylesheet and also showing Qt.BackgroundColorRole colors based on my logic?

[Edit] Added this minimal code sample:

import string
import numpy as np
import random
import typing
import pandas as pd
import sys

from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import Qt

letters = list(string.ascii_uppercase + string.digits)


class SimpleDelegate(QtWidgets.QStyledItemDelegate):
    def paint(self, painter: QtGui.QPainter, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> None:
        painter.save()
        painter.drawPixmap(option.rect.topLeft(), QtGui.QPixmap("cell_bg.png"))
        painter.restore()


class MyModel(QtCore.QAbstractTableModel):
    def __init__(self, table_data, header_labels):
        super(MyModel, self).__init__()
        self._data: 'pd.DataFrame' = table_data
        self.header_labels = header_labels
        self.num_c_bg_props = {'h': 240, 's': 170}  # blue for high values

    def populate(self, df):
        """Reset the model"""
        self.beginResetModel()
        self._data = df.copy()
        self.endResetModel()

    def data(self, index: QtCore.QModelIndex, role: int = ...) -> typing.Any:
        if role == Qt.DisplayRole:
            return str(self._data.iat[index.row(), index.column()])

        elif role == Qt.BackgroundColorRole:
            if self.header_labels[index.column()] == "Name":
                return QtGui.QColor(0, 255, 110)  # black
            text = self._data.iat[index.row(), index.column()]
            max_col_value = self._data.iloc[:, index.column():index.column() + 1].max()
            min_col_value = self._data.iloc[:, index.column():index.column() + 1].min()
            intensity_ratio = np.divide(np.subtract(float(text), min_col_value),
                                        np.subtract(max_col_value, min_col_value))

            hsv_value = intensity_ratio * 255
            hsv_c_tuple = (self.num_c_bg_props['h'], self.num_c_bg_props['s'], int(hsv_value))
            return QtGui.QColor.fromHsv(*hsv_c_tuple)

        elif role == Qt.ForegroundRole:
            if self.header_labels[index.column()] == "Name":
                return QtGui.QColor(0, 0, 0)  # black
            else:
                return QtGui.QColor(255, 255, 255)  # white

    def rowCount(self, parent: QtCore.QModelIndex = ...) -> int:
        return len(self._data)

    def columnCount(self, parent: QtCore.QModelIndex = ...) -> int:
        return len(self.header_labels)

    def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...) -> typing.Any:
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return str(self.header_labels[section]).upper()
            if orientation == Qt.Vertical:
                return str(section)


class MyTableView(QtWidgets.QTableView):
    def __init__(self):
        super(MyTableView, self).__init__()
        header_labels = ("Name", "Col1", "Col2", "Col3")
        df = pd.DataFrame(columns=header_labels)
        for i in range(10):
            random.shuffle(letters)
            _row = [''.join(letters[:5])]
            for _ in range(3):
                _row.append(random.random())
            df = df.append(dict(zip(header_labels, _row)), ignore_index=True)
        self._model = MyModel(df, header_labels)
        self.setModel(self._model)

        self.setItemDelegateForColumn(0, SimpleDelegate())

        fit_to_view_shortcut = QtWidgets.QShortcut("Ctrl+R", self)
        fit_to_view_shortcut.activated.connect(self.fit_to_view)

    def fit_to_view(self):
        tot_size = self.visibleRegion().boundingRect().width()
        per_col_width = tot_size // 4
        for _ in range(4):
            self.setColumnWidth(_, per_col_width)


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

        tableView_1 = MyTableView()
        self.setCentralWidget(tableView_1)
        # ::item -> background-image property replace all colors set by code logic
        tableView_1.setStyleSheet(
            """
QHeaderView{
    background-color: transparent; /* header background*/
    color: white;
}
QTableView{
    gridline-color: rgba(0,0,0,0);
}
QTableView::item{
    color: white;
    /*background-image: url(cell_bg.png); /*cell bg image*/*/   
    padding-left: 5px; /*cell spacing*/
    border: 2px solid transparent; /*transparent cell borders*/
    border-radius: 10px;
}
QHeaderView::section {
    color: white;
    background-color: #343434;
    font-size: 10pt;
    padding: 2px;
    border: 1px solid #4a4a4a;
    margin: 2px;
}""")


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

::item background image used: cell_bg.png

python

pyqt5

qtableview

qtstylesheets

0 Answers

Your Answer

Accepted video resources