I subclassed a QTreeView and I have two columns where there are checkboxes. I would like to set two different images: one for the first column, and another one for the second column. I know I can change the image in the stylesheet with:
QTreeView::indicator:checked{
image: url(:/checked);
}
QTreeView::indicator:unchecked{
image: url(:/unchecked);
}
but it will change all the checkboxes in the tree view. Is there a way to do it with the stylesheets, or do I need to use a delegate?
Short answer: Stylesheets can't do that (as far as I know). They are a pretty immature feature in Qt, and there seems to be no development on them either.
What you can do:
You cannot assign properties to a column or an item and you cannot access the columns by index.
But you can use some of the pseudo-states selectors like :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 the sake of simplicity.
Note however, that these pseudo-states are actual currently visible states, so if the user is allowed to reorder columns, the style of the column might change. For example if the user drags one of the :middle
columns and drops it at the end, the box will not be blue anymore.
You can fake it using Qt::DecorationRole
.
To do so, you have to receive the mousePressEvent
either by subclassing QTreeView
or by installing an event filter. You can then change the icon (via Qt::DecorationRole
+ emit dataChanged()
) when the user clicks in the area of the icon.
This does not work with the keyboard of course.
Subclass QStyledItemDelegate
and override paint()
, just as you suggested.
If you are creating a heavily styled application, you probably have to create a custom Style sooner or later. Stylesheets just don't support some features.
To do so, subclass QProxyStyle
, override drawPrimitive
and handle the drawing if QStyle::PE_IndicatorViewItemCheck
is passed. You will also receive a QStyleOptionViewItem
of which has some useful properties like checkState
, features
(contains QStyleOptionViewItem::HasCheckIndicator
if there is a checkbox), and of course index
so you can determine what kind of checkbox you want to draw.
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. Simply paint over the checkbox drawn by QStyledItemDelegate. Requires you to fill the whole box however, otherwise the original will be visible.
You can try to use QStyledItemDelegate to draw anything but the checkbox, and draw the checkbox afterwards, but that is a little harder and will leave you with some minor drawing artifacts if you don't want to spend too much time on it.
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 way is much simpler, cleaner and has none of the drawbacks. You can apply the style globally, or only for a single widget.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With