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

Posted

技术标签:

【中文标题】QTableview,PySide2中单元格的背景颜色【英文标题】:background color of cells in QTableview, PySide2 【发布时间】:2019-02-25 15:41:40 【问题描述】:

是否可以使用 PySide2 有条件地更改 QTableView 中项目的背景颜色? 我在model view framework 上阅读了很多内容。我不知道是否有必要使用委托。最近,我能够在没有代表的情况下获得一列复选框。我相信虚拟方法setItemData(index, roles)itemData(index) 可能是我需要的。但是,PySide2 中没有QMap。我的模型必须需要某个地方来存储QtCore.Qt.BackgroundRole 使用的额外信息(那个枚举,顺便说一句,说“用于使用默认委托渲染的项目的背景画笔”)如果我没有指定委托,是“默认委托”使用?我应该改用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

【问题讨论】:

【参考方案1】:

默认情况下,委托使用 BackgroundRole 信息(如果可用),因此解决方案只是返回 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")

【讨论】:

@ErikIverson 如果我的回答对您有帮助,请不要忘记将其标记为正确,如果您不知道该怎么做,请查看tour。 我确实将其标记为已接受。我只有 25 业力点,所以可能不会被其他人接受。 抱歉耽搁了@eyllanesc,我终于注意到了复选标记

以上是关于QTableview,PySide2中单元格的背景颜色的主要内容,如果未能解决你的问题,请参考以下文章

更改值后使用代理模型更改 QTableView 的单元格的背景颜色

更改 QTableModel 中单元格的背景颜色

如何在 QTableView 中发出输入单元格和离开单元格的信号

如何在 QTableview 单元格的值更新后及时为它的颜色设置动画?

Qt C++ 从 QTableView 中获取选定行的每个单元格的数据

如何使用 pyside 在 QtableView 单元格中设置数据