QTableview,PySide2中单元格的背景颜色

background color of cells in QTableview, PySide2

是否可以使用 PySide2 有条件地更改 QTableView 中项目的背景颜色? 我在 model view framework 上读了很多。我不知道是否有必要使用委托。最近我能够在没有委托的情况下获得一列复选框。我相信虚拟方法 setItemData(index, roles)itemData(index) 可能是我所需要的。但是,PySide2 中没有QMap。我的模型必须需要在某个地方存储 QtCore.Qt.BackgroundRole 使用的额外信息(那个枚举,顺便说一句,说 "the background brush used for items rendered with the default delegate")如果我没有指定委托,是否使用了 "default delegate"? .我应该改用 QStandardItemModel 吗? 在下面的示例代码中,我如何根据某些阈值将特定列的背景颜色设为红色(最小和最大列是阈值?

from PySide2.QtWidgets import (QWidget, QApplication, QTableView,QVBoxLayout)
import sys
from PandasModel2 import  PandasModel2
import numpy as np
import pandas as pd
class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(300, 300, 700, 300)
        self.setWindowTitle("QTableView")
        self.initData()
        self.initUI()

    def initData(self):

        data = pd.DataFrame(np.random.randint(1,10,size=(6,4)), columns=['Test#','MIN', 'MAX','MEASURED'])
        data['Test#'] = [1,2,3,4,5,6]                    
        #add the checkable column to the DataFrame
        data['Check'] = True
        self.model = PandasModel2(data)

    def initUI(self):
        self.tv = QTableView(self)
        self.tv.setModel(self.model)
        vbox = QVBoxLayout()
        vbox.addWidget(self.tv) 
        self.setLayout(vbox)  

app = QApplication([])
ex = Example()
ex.show()
sys.exit(app.exec_())

我有一个使用 pandas dataFrame 的自定义模型:

import PySide2.QtCore as QtCore
class PandasModel2(QtCore.QAbstractTableModel):
    """
    Class to populate a table view with a pandas dataframe.
    This model is non-hierachical. 
    """
    def __init__(self, data, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self._data = data

    def rowCount(self, parent=None):
        return self._data.shape[0]

    def columnCount(self, parent=None):
        return self._data.shape[1]

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role==QtCore.Qt.DisplayRole:
            if index.column() != 4: 
            #don't want what determines check state to be shown as a string
                if index.isValid():              
                    if index.column() in [1,2,3]:
                        return '{:.3f}'.format(self._data.iloc[index.row(), index.column()])    
                    if index.column() == 0:
                        return '{:.2f}'.format(self._data.iloc[index.row(), index.column()])
                    return str(self._data.iloc[index.row(), index.column()])
        if role==QtCore.Qt.CheckStateRole:  
            if index.column()==4:#had to add this check to get the check boxes only in column 10
                if self._data.iloc[index.row(), index.column()] == True:
                    return QtCore.Qt.Checked
                else:
                   return QtCore.Qt.Unchecked

    def getMinimum(self, row):
        return self._data.iloc[row, self.getColumnNumber('MIN')]
    def getMaximum(self, row):
        return self._data.iloc[row, self.getColumnNumber('MAX')]

    def getColumnNumber(self, string):
        '''
        Given a string that identifies a label/column, 
        return the location of that label/column.
        This enables the config file columns to be moved around. 
        '''
        return self._data.columns.get_loc(string)

    def headerData(self, col, orientation, role):
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return self._data.columns[col]
        return None
    def flags(self, index):
        '''
        The returned enums indicate which columns are editable, selectable, 
        checkable, etc. 
        The index is a QModelIndex. 
        '''
        if index.column() == self.getColumnNumber('Check'):
            #print(index.column())
            return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsUserCheckable
        else:
            return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable
        return QtCore.Qt.ItemIsEnabled

    def setData(self, index, value, role=QtCore.Qt.DisplayRole):
        """Set the value to the index position depending on Qt::ItemDataRole and data type of the column
        Args:
            index (QtCore.QModelIndex): Index to define column and row.
            value (object): new value.
            role (Qt::ItemDataRole): Use this role to specify what you want to do.
        Raises:
            TypeError: If the value could not be converted to a known datatype.
        Returns:
            True if value is changed. Calls layoutChanged after update.
            False if value is not different from original value.
        """
        if not index.isValid(): 
            return False
        if role == QtCore.Qt.DisplayRole: #why not edit role?
            self._data.iat[index.row(),index.column()]= value
            self.layoutChanged.emit()
            return True
        elif role == (QtCore.Qt.CheckStateRole | QtCore.Qt.DisplayRole):
            #this block does get executed when toggling the check boxes, 
            #verified with debugger. Although the action is the same 
            #as the block above! 
            self._data.iat[index.row(),index.column()]= value
            self.layoutChanged.emit()
            return True
        else:
            return False

代理默认使用 BackgroundRole 信息(如果可用),因此解决方案只是 return QColor、QBrush 或类似的。

from PySide2 import QtCore, QtGui

class PandasModel2(QtCore.QAbstractTableModel):
    # ...
    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return
        if not (0 <= index.row() < self.rowCount() and 0 <= index.column() <= self.columnCount()):
            return
        value = self._data.iloc[index.row(), index.column()]
        if role == QtCore.Qt.DisplayRole:
            if index.column() != 4: 
                if index.column() in [1,2,3]:
                    return '{:.3f}'.format(value)    
                if index.column() == 0:
                    return '{:.2f}'.format(value)
                return str(value)
        elif role == QtCore.Qt.CheckStateRole:  
            if index.column() == 4:
                return QtCore.Qt.Checked if value else QtCore.Qt.Unchecked
        elif index.column() == self.getColumnNumber('MEASURED'):
            if role == QtCore.Qt.BackgroundRole:
                if self.getMinimum(index.row()) <= value <= self.getMaximum(index.row()):
                    return QtGui.QColor("red")