How to style QTreeView items by role with CSS ?



  • Hello,
    I have a QTreeView and a custom model in an application which uses a custom CSS stylesheet.
    I want to color some items in my tree view depending on their Qt::ForegroundRole, which returns a non-null color in some specific cases.
    However it seems CSS overrides this and my items don't change color.

    So I wondered if it's possible, like in HTML, to put "CSS classes" on items so I can style them directly in CSS for the special cases my application defines. But I have no clue how to do that, especially with model-view system...

    So I tried QStyledItemDelegate, but again I don't find many examples about how to do this simple thing.
    I have this so far:

    void TextColorDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex &index) const
    {
    	QVariant d = index.data(Qt::ForegroundRole);
    	if (d.type() != QVariant::Color)
    		QStyledItemDelegate::paint(painter, option, index);
    	else
    	{
    		QColor color = d.value<QColor>();
    		QBrush brush = painter->brush();
    		brush.setColor(color);
    		painter->setBrush(brush);
    		QString text = index.data(Qt::DisplayRole).toString();
    		painter->drawText(option.rect, 0, text);
    	}
    }
    

    But all it does is draw a black text with fixed background even when selected, hovered etc.
    Any ideas?


  • Lifetime Qt Champion

    Hi,

    Do you mean something like described here ?


  • Moderators

    @Zylann
    it's not possible to style items in itemview widgets individually by Qt.

    The only solution is:

    1. add a string property to your tree view
    2. define a simple syntax to define the font colors for your data role values
    3. in the property setter parse the string and set it to the item delegate
    4. subclass initStyleOption() in the delegate (see bleow)
    virtual void MyItemDelegate::initStyleOption(QStyleOptionViewItem * option, const QModelIndex & index) const
    {
         QStyledItemDelegate::initStyleOption(option, index);
    
         if( index.data( MY_ITEM_DATA_ROLE ) == ??? )
         {
                QPalette::ColorGroup cg = option->state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
            if( cg == QPalette::Normal && !(option->state & QStyle::State_Active) )
                cg = QPalette::Inactive;
            QColor fontColor = ....;
            option->palette.setColor(cg, option->state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text, fontColor);
         }
    }
    


  • @SGaist Yes, theoretically the correct way for the designer would be the ability to wrote something like:

    QTreeView::item:mycustomstate {
       color: #abc;
    }
    

    But at the moment, if I could just get the color to change according to Qt::ForegroundRole, it would be a good start too.

    @raven-worx My model can already return a color when I query data(Qt::ForegroundRole).
    I modified my column delegate so it overrides initStyleOption() like you did, but it has no effect.
    What do you mean by adding a string property to the tree view? Subclass QTreeView? What would be this property for?


  • Moderators

    @Zylann
    The property was for styling via stylesheets.
    But now you say that the color comes from the model.

    Please show your initStyleOption() implementation.



  • void TextColorDelegate::initStyleOption(QStyleOptionViewItem * option, const QModelIndex & index) const
    {
    	QStyledItemDelegate::initStyleOption(option, index);
    
    	QVariant d = index.data(Qt::ForegroundRole);
    	if (d.type() == QVariant::Color)
    	{
    		QColor color = d.value<QColor>();
    
    		QPalette::ColorGroup cg = option->state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
    		if (cg == QPalette::Normal && !(option->state & QStyle::State_Active))
    		{
    			cg = QPalette::Inactive;
    		}
    
    		option->palette.setColor(cg, option->state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text, color);
    	}
    }
    

    I tried again without CSS, I get colors. With CSS, no colors but those from CSS.
    I also realized I was setting the proxy before the model to be set, so now I set it after, but that didn't fixed the problem.


  • Moderators

    @Zylann
    are you sure you are successfully passing the condition d.type() == QVariant::Color?
    Because i am almost sure the code works :)
    Also the ForegroundRole would be used already by Qt when you remove the stylesheet.



  • Everything is fine and works when I remove the CSS. But with CSS, I don't get that because the style overrides my code.
    I even tried to remove the if and hardcode QColor(0,255,0) for every case, with no luck.
    The only time I got something look "different" is by overriding paint() (see my first post), but it also broke many other things, not only the text color.



  • I finally managed to get custom colors. I gave my QTreeView an object name to be able to write this in CSS:

    m_treeView->setObjectName("MyTreeView");
    m_treeView->setStyleSheet("QTreeView#MyTreeView::item {color: none;}");
    

    basically now my model controls text color through Qt::ForegroundRole regardless of the application's CSS.
    I feel like it's the wrong place to put theming, but it works for me at the moment.
    Well... until we decide to have different themes :-°



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.