2
votes

I create a class 'pandasModel' based on QAbstractTableModel, shown below:

import sys
from PyQt5.QtGui     import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore    import *
class pandasModel(QAbstractItemModel):

    def __init__(self, data, parent=None):
        QAbstractItemModel.__init__(self, parent)
        self._data = data

    def rowCount(self, parent=None):
        return self._data.index.size

    def columnCount(self, parent=None):
        return self._data.columns.size

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            if role == Qt.DisplayRole:
                return str(self._data.iloc[index.row(), index.column()])
            if role == Qt.EditRole:
                return str(self._data.iloc[index.row(), index.column()])
        return None

    def headerData(self, rowcol, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self._data.columns[rowcol]
        if orientation == Qt.Vertical and role == Qt.DisplayRole:
            return self._data.index[rowcol]
        return None

    def flags(self, index):
        flags = super(self.__class__, self).flags(index)
        flags |= Qt.ItemIsEditable
        flags |= Qt.ItemIsSelectable
        flags |= Qt.ItemIsEnabled
        flags |= Qt.ItemIsDragEnabled
        flags |= Qt.ItemIsDropEnabled
        return flags

    def sort(self, Ncol, order):
        """Sort table by given column number.
        """
        try:
            self.layoutAboutToBeChanged.emit()
            self._data = self._data.sort_values(self._data.columns[Ncol], ascending=not order)
            self.layoutChanged.emit()
        except Exception as e:
            print(e)

Also I create a QTableView to show the Model, shown below:

class TableWin(QWidget):
    pos_updown = -1
    pos_save = []

    def __init__(self):
        super(TableWin, self).__init__()
        self.resize(200, 100)
        self.table = QTableView(self)
        self.v_layout = QVBoxLayout()
        self.v_layout.addWidget(self.table)
        self.setLayout(self.v_layout)
        self.showdata()

    def showdata(self):
        data = pd.DataFrame([[1,2,3,4],[5,6,7,8]])
        model = pandasModel(data)
        self.table.setModel(model)

    def set_cell_color(self, row, column)
        '''
          Pass two arguments to this function, which is called to set
          the background color of the cell corresponding to the row and column
        '''
if __name__ == '__main__':
    app = QApplication(sys.argv)
    tableView = TableWin()
    # I want to change cell's color by call function 'set_cell_color' here
    # tableView.set_cell_color(row=1,column=1) 
    tableView.show()
    sys.exit(app.exec_())

We can show data in QTableview now, but question is how can i call function 'set_cell_color' to set the background color for cell with given row and column, so could you please tell me how to finish the code in def set_cell_color?

once i want to set cell's color by using 'model.item(row, col).setBackground(QColor(240, 255, 240))' just like QStandardItemModel, but raise error ''model' has no attribute 'item''

this link shows a method to set cell's color

code shows below:

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


class Model(QAbstractTableModel):
    def __init__(self, parent=None):
        super(Model, self).__init__(parent)
        self._data = [[['%d - %d' % (i, j), False] for j in range(10)] for i in range(10)]

    def rowCount(self, parent):
        return len(self._data)

    def columnCount(self, parent):
        return len(self._data[0])

    def flags(self, index):
        return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable

    def data(self, index, role):
        if index.isValid():
            data, changed = self._data[index.row()][index.column()]

        if role in [Qt.DisplayRole, Qt.EditRole]:
            return data

        if role == Qt.BackgroundRole and data == "In error":        # <---------
            return QBrush(Qt.red) 

    def setData(self, index, value, role):
        if role == Qt.EditRole:
            self._data[index.row()][index.column()] = [value, True]
            self.dataChanged.emit(index, index)
            return True
        return False

if __name__ == '__main__':
    app = QApplication(sys.argv)
    tableView = QTableView()
    m = Model(tableView)
    tableView.setModel(m)
    tableView.show()
    sys.exit(app.exec_())

Use 'return QBrush(Qt.red)' in 'data' function upon can set the background-color of cells with value 'In error', but the background-color was already set when the Qtableview finish created, i just want to set cell's background color when i call function 'set_cell_color' ,that means i can control the cell's background even after Qtableview already been created, i will really appreciate for your help.

1

1 Answers

2
votes

The logic is to save the information in the model associating the item's position and the item's color, and to update it, the dataChanged signal must be emitted.

Note:Your model is of type table so you must inherit from QAbstractTableModel and not from QAbstractItemModel

Considering the above, the solution is:

class pandasModel(QAbstractTableModel):
    def __init__(self, data, parent=None):
        QAbstractItemModel.__init__(self, parent)
        self._data = data

        self.colors = dict()

    def rowCount(self, parent=None):
        return self._data.index.size

    def columnCount(self, parent=None):
        return self._data.columns.size

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            if role == Qt.DisplayRole:
                return str(self._data.iloc[index.row(), index.column()])
            if role == Qt.EditRole:
                return str(self._data.iloc[index.row(), index.column()])
            if role == Qt.BackgroundRole:
                color = self.colors.get((index.row(), index.column()))
                if color is not None:
                    return color
        return None

    def headerData(self, rowcol, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self._data.columns[rowcol]
        if orientation == Qt.Vertical and role == Qt.DisplayRole:
            return self._data.index[rowcol]
        return None

    def flags(self, index):
        flags = super(self.__class__, self).flags(index)
        flags |= Qt.ItemIsEditable
        flags |= Qt.ItemIsSelectable
        flags |= Qt.ItemIsEnabled
        flags |= Qt.ItemIsDragEnabled
        flags |= Qt.ItemIsDropEnabled
        return flags

    def sort(self, Ncol, order):
        """Sort table by given column number.
        """
        try:
            self.layoutAboutToBeChanged.emit()
            self._data = self._data.sort_values(
                self._data.columns[Ncol], ascending=not order
            )
            self.layoutChanged.emit()
        except Exception as e:
            print(e)

    def change_color(self, row, column, color):
        ix = self.index(row, column)
        self.colors[(row, column)] = color
        self.dataChanged.emit(ix, ix, (Qt.BackgroundRole,))


class TableWin(QWidget):
    pos_updown = -1
    pos_save = []

    def __init__(self):
        super(TableWin, self).__init__()
        self.resize(200, 100)
        self.table = QTableView(self)
        self.v_layout = QVBoxLayout()
        self.v_layout.addWidget(self.table)
        self.setLayout(self.v_layout)
        self.showdata()

    def showdata(self):
        data = pd.DataFrame([[1, 2, 3, 4], [5, 6, 7, 8]])
        self.model = pandasModel(data)
        self.table.setModel(self.model)

    def set_cell_color(self, row, column):
        self.model.change_color(row, column, QBrush(Qt.red))