I have a QListView which uses a model to display custom Alarm objects as text applying different colors based on the Alarm.level attribute of each object.
@dataclass
class Alarm:
name: str
level: int
started_at: datetime
class Alarm_Viewer_Model(QAbstractListModel):
def __init__(
self,
alarms: List[Alarm] = None,
alarm_colors: Dict[int, QColor] = None,
*args,
**kwargs
):
super().__init__(*args, **kwargs)
self._alarms = alarms or []
self._alarm_colors = alarm_colors or {}
def data(self, index: QModelIndex, role: int):
alarm = self._alarms[index.row()]
if role == Qt.DisplayRole:
dt = alarm.started_at.strftime('%Y/%m/%d %H:%M:%S')
return f"{dt} - {alarm.level.name} - {alarm.name}"
elif role == Qt.ForegroundRole:
alarm_color = self._alarm_colors.get(alarm.level, Qt.black)
return QBrush(alarm_color)
This works fine until I try load a stylesheet which applies a default text color to all widgets. In this case the stylesheet color overrides whatever color is set by the model (other questions, like this or this, confirm that this is normal Qt behaviour).
My question is: how can I still have control over the style of each list item while a generic style is applied via the stylesheet?
One way I tried to do this (which is also suggested in one of the links) is to have the model set a custom property on the item which could be accessed by the stylesheet with an appropriate selector (e.g.
QListView::item[level=1] { color: red; }). However, I could not find any way to set the property in the list view item: is it even possibile?Another way that was suggested (but have not yet tried) is via QStyledItemDelegate. However from what I have seen, even if it works, it looks way overkill: is it the only way to solve this?
PS: I tagged this with PySide2 as I am using it, but am more interested in the general Qt behaviour: finding a way to implement it in PySide2 specifically is a secondary problem for now.
I got it to work: I am not sure this is the best way to do it or if it has any drawbacks, but it is fine for what I wanted.
I implemented a
QStyledItemDelegatewith a custompaint()method which draws the widget underneath according to the stylesheet and then draws text onto it according to theForegroundRoledata provided by the model.The only downside is that I cannot fully control the styling from the stylesheet, but have to provide a
Alarm.level->QColormap to the model programmatically.I tried to circumvent the inability to set properties on the list items by doing
option.widget.setProperty("alarm-level", alarm.level)in the paint method, but it did not work, unfortunately.If anyone has any comment on this solution or ideas on how to control the styling from the stylesheet I'll gladly hear it.