How to prevent Qt from alpha-blending the selection over icons in QListView?

547 views Asked by At

I have a Qt application that uses a QTableView and a QListView. In both witdgets I display some icons.

The problem is that when the user selects one of the entries Qt alpha blends the selection over the icons making the icons appear with less contrast and slightly different colors.

I want Qt to draw the selection first and then draw the icons on top so they look the same no matter if they are selected or not. How can I do that?

Update:

This is what my problem looks like:

Effect with a normal icon

It is easier to spot with a white rectangular icon:

Effect with a white rectangle icon

When you check the color value with a graphics editor you see that the color value changes. I would like Qt to paint the icon as is without this overlay.

The QListView here is styled with a color gradient background. But I have the same problem with a QTableView which is unstyled.

All icons are SVG by the way.

2

There are 2 answers

0
Joseph Artsimovich On BEST ANSWER

The drawing of ItemView elements is handled by a delegate. You can set a custom delegate for a column, a row, or for individual items. The default implementation just delegates drawing to the widget's style:

void QStyledItemDelegate::paint(QPainter *painter,
    const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_ASSERT(index.isValid());

    QStyleOptionViewItem opt = option;
    initStyleOption(&opt, index);

    const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
    QStyle *style = widget ? widget->style() : QApplication::style();
    style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
}

You can find the actual code that does the drawing here. It seems that all styles shipped with Qt5 ultimately delegate the drawing of ItemView elements to that code. What's interesting is that code in question actually draws the icon on top of the selection, so you must be using some custom style. The call that draws the selection is:

proxy()->drawPrimitive(PE_PanelItemViewItem, opt, p, widget);

That call goes before all the other drawing calls.

In your custom delegate you should be able to ask the style to draw everything except the icon, by modifying QStyleOptionViewItem opt (see the first code snippet). Then you can draw you icon manually, the same way QCommonStyle does it.

0
Natsukane On

As an alternate solution that's IMO more straightforward and seems to work regardless of the active QStyle (further testing/confirmation needed), you can simply subclass QIcon:

from PyQt5 import QtCore, QtGui

class StaticIcon(QtGui.QIcon):
    def __init__(self, filePath):
        super().__init__(filePath)
        self.addFile(filePath, QtCore.QSize(), QtGui.QIcon.Active)
        self.addFile(filePath, QtCore.QSize(), QtGui.QIcon.Selected)

...explicitly overriding the active and selected state icons with the normal state icon, after they're automatically set by the style.