QTableView item color to vary by value
I need to have the colors of items shown in a
QTableViewvary depending on the value in each cell. [Specifically, some values/columns are monetary amounts, if the amount in a cell is negative I want it in red, else the default black.]
As some of you may know, I do research things, so you may be assured I have considered different approaches. I do not need detailed code for an answer, but I do need to know what approach you are recommending.
Some of my constraints include:
The code has to be generic for all my (sub-classed)
QTableViews. I may need for
QTableWidgets too. Therefore I do not know in advance which columns will need this.
My tables can have any model type from:
QStandardItemModel. Solution must work for all 3.
I can recognise a "monetary value" by the fact that the value is "a floating point number". For me, all such values will be monetary.
I suspect/presume it comes down to a choice between
My natural preference, for both simplicity & general applicability, is
data(ForegroundRole), but I admit I'm finding it messy when that depends on the value.
At this point I'd really like to put this question out there to see whether (a) anybody is going to answer(!) and (b) what they're going to suggest. Then I'll respond from there. TIA!
What about a proxy model like QIdentityProxyModel ?
JonB last edited by
You are a man of few words! :) I have read up about
QIdentityProxyModel. Let's see if I understand what you are suggesting.
For example, a proxy model could be created to define the font used, or the background colour, or the tooltip etc. This removes the need to implement all data handling in the same class that creates the structure of the model, and can also be used to create re-usable components.
So, I think this could be used because I said I have a variety of model types. This would then allow me to put the logic for foreground color/role in one place, rather than in each of my 3 sub-classes of these. Is that what you had in mind?
However, that itself is not my problem. My question revolves around the fact that in order to decide what color I want I need to look at the value of each item. This is the case whether I implement via
QStyledItemDelegate(which represent very different ways of approaching setting the color).
So, for example, if I go down my (preferred) route of doing it by role instead of render delegate, whether or not I use a proxy model I will end with code for sub-classed
model.data()which has to look like:
def data(self, index: QtCore.QModelIndex, role: QtCore.Qt.ItemDataRole=QtCore.Qt.DisplayRole) -> typing.Any: if role == Qt.ForegroundRole: dataVal = super().data(index, Qt.EditRole): if isinstance(dataVal, float): if dataVal < 0.0: return QtGui.QColor(QtCore.Qt.red) ...
The point here is that when called for
data(index, ForegroundRole)my code has to in turn call
data(index, EditRole)to retrieve the data value in order to decide what to return for the color. I would still need to do same access to data value if I did my color via
That is what "worries" me. I don't know how "costly" that is, or whether there is any better way. If this is "fine" & "normal" for accessing the data value when deciding the color then please say so and I am good to go?
I would say that this is the normal way but likely not the most efficient.
One alternative would to store the foreground color when a value changes. It will cost memory in place of time spent to access each value when the role is requested.
Thank you for replying.
I have implemented the
data()approach and it works.
I did consider overriding
setData()to store the color against the value at assignment time. I rejected it for 2 reasons:
QSqlTableModel, query result set population do not cause
setData()to be called. So colors would not get set initially.
QSqlQueryModel, as above, plus as it's read-only, there is a
data()but not a
Am I right about these? Did you mean that I could have written the code in slot for
QAbstractItemModel::dataChangedsignal instead of overriding
dataChangedis only a signal, but yes, you could connect it to a slot to update the foreground color if you keep it somewhere.
Yes, I re-phrased. Last question, promise: would query/table models emit it as result set is read back in, whereas
setData()does not get called there? [Cannot test atm.]
Sorry, I'm not sure I'm following that last question.
When result set is received from db via something like
QSqlQueryModel::setQuery()rows/columns in model are populated. I believe
setData()is not called/involved. How would I recognise this (so as to populate colors for initial values received): will model emit
dataChanged(), or would I need to handle
Since it's pretty heavy change, I would check for the modelReset signal in this case.
JonB last edited by
OK, it was really for interest, thanks for your time. In my case as I said I have implemented via calculated return result from
data()rather than setting the color by pre-storing it. It seems efficient enough for my purposes.