Set different images for different checkboxes in QTreeView

I have subclassed QTreeView and I have two columns where there are checkboxes. I would like to set two different images: one for the first column, the other for the second column. I know that I can change the image in the stylesheet using

QTreeView::indicator:checked{ image: url(:/checked); } QTreeView::indicator:unchecked{ image: url(:/unchecked); } 

but it will change all the flags in the tree view. Is there a way to do this with styles, or do I need to use a delegate?

+5
source share
1 answer

Short answer: Style sheets cannot do this (as far as I know). This is a pretty immature function in Qt, and there seems to be no development either.

What can you do:

Stylesheets

You cannot assign properties to a column or element, and you cannot access columns by index.

But you can use some pseudo-state selectors, such as :first :middle and :last :

 QTreeView::indicator:first:checked{ background: red; } QTreeView::indicator:middle:checked{ background: blue; } QTreeView::indicator:unchecked{ background: lightgray; } 

I used colors instead of images for simplicity. Please note, however, that these pseudo-states are currently valid visible states, so if the user is allowed to reorder the columns, the style of the column may change. For example, if a user drags one of the columns :middle and drops it at the end, the field will no longer be blue.

DecorationRole

You can fake it with Qt::DecorationRole .

To do this, you must get mousePressEvent either by subclassing QTreeView or by setting an event filter . Then you can change the icon (via Qt::DecorationRole + emit dataChanged() ) when the user clicks in the icon area.

This does not work with the keyboard, of course.

Custom ItemDelegate

Subclass QStyledItemDelegate and override paint() , as you expected.

Custom style

If you are creating an application with a hard style, you may have to create your own style sooner or later. Style sheets simply do not support some features.

To do this, subclass QProxyStyle , override drawPrimitive and process the drawing if QStyle::PE_IndicatorViewItemCheck passed. You will also get a QStyleOptionViewItem that has some useful properties such as checkState , features (contains QStyleOptionViewItem::HasCheckIndicator if there is a check box) and, of course, index so you can determine which check box you want to draw.

Edit: Application

Custom QStyledItemDelegate Example

 void MyItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { QStyledItemDelegate::paint(painter, option, index); QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget); drawCheckBox(painter, checkRect, opt.checkState, index); } void MyItemDelegate::drawCheckBox(QPainter * painter, const QRect & checkRect, Qt::CheckState checkState, const QModelIndex & index) const { if (checkState == Qt::Checked) { switch (index.column()) { case 0: painter->fillRect(checkRect, Qt::red); break; default: painter->fillRect(checkRect, Qt::blue); } } else { painter->fillRect(checkRect, Qt::lightGray); } } 

This example is quick and easy. Just draw the checkbox drawn by QStyledItemDelegate. It is required that you fill the entire cell, but otherwise the original will be visible.

You can try using QStyledItemDelegate to draw anything other than a checkbox, and then mark the checkbox, but it is a little more complicated and will leave you with some minor drawing artifacts if you don't want to spend too much time on it.

Custom QProxyStyle Example

 void MyStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption * opt, QPainter * p, const QWidget * w) const { if (pe == QStyle::PE_IndicatorViewItemCheck) { const QStyleOptionViewItem * o = static_cast<const QStyleOptionViewItem *>(opt); drawCheckBox(p, opt->rect, o->checkState, o->index); return; } QProxyStyle::drawPrimitive(pe, opt, p, w); } 

The drawCheckBox() function is the same as in the first example. As you can see, this method is much simpler, cleaner and does not have any of the disadvantages. You can apply a style all over the world or for just one widget.

+5
source

Source: https://habr.com/ru/post/1272697/


All Articles