[SOLVED] Multiple background images in QTreeView
-
Hi,
I am making an audio software on my free time and I would like to display properly multiple background images in a QTreeView.My tree view is structured in Artist \ Album \ Tracks (can be changed) and here is what I have done so far :
To do this, I have reimplemented
void QTreeView::drawBranches(QPainter * painter, const QRect & rect, const QModelIndex & index) const void LibraryTreeView::drawBranches(QPainter *painter, const QRect &r, const QModelIndex &proxyIndex) const { SettingsPrivate *settings = SettingsPrivate::instance(); if (settings->isBigCoverEnabled()) { QModelIndex index2 = proxyIndex; QStandardItem *item = _libraryModel->itemFromIndex(_proxyModel->mapToSource(proxyIndex)); if (item && item->type() == LibraryFilterProxyModel::IT_Album && isExpanded(index2)) { QString cover = item->data(LibraryFilterProxyModel::DF_CoverPath).toString(); /* Get the area to display cover */ int w, h; w = rect().width() - (r.width() + 2 * verticalScrollBar()->width()); h = item->rowCount() * this->indexRowSizeHint(index2.child(0, 0)); QPixmap pixmap(cover); w = qMin(h, qMin(w, pixmap.width())); QPixmap leftBorder = pixmap.copy(0, 0, 3, pixmap.height()); leftBorder = leftBorder.scaled(1 + rect().width() - (w + 2 * verticalScrollBar()->width()), w); /* Create a mix with 2 images: first one is a 3 pixels subimage of the album cover which is expanded to the left border */ /* The second one is a computer generated gradient focused on alpha channel */ if (!leftBorder.isNull()) { QLinearGradient linearAlphaBrush(0, 0, leftBorder.width(), 0); linearAlphaBrush.setColorAt(0, QApplication::palette().base().color()); linearAlphaBrush.setColorAt(1, Qt::transparent); painter->save(); /* Because the expanded border can look strange to one, is blurred with some gaussian function */ QImage img = ImageUtils::blurred(leftBorder.toImage(), leftBorder.rect(), 10, false); painter->drawImage(0, r.y() + r.height(), img); painter->setCompositionMode(QPainter::CompositionMode_SourceOver); painter->setPen(Qt::NoPen); painter->setBrush(linearAlphaBrush); painter->drawRect(0, r.y() + r.height(), leftBorder.width(), leftBorder.height()); painter->drawPixmap(1 + rect().width() - (w + 2 * verticalScrollBar()->width()), r.y() + r.height(), w, w, pixmap); painter->setOpacity(settings->bigCoverOpacity()); painter->fillRect(0, r.y() + r.height(), rect().width() - 2 * verticalScrollBar()->width(), leftBorder.height(), QApplication::palette().base()); painter->restore(); } } } TreeView::drawBranches(painter, r, proxyIndex); }
Few issues with my implementation that I'm struggling with :
- when scrolling down, if the node "Album" is out of sight, the faded background image is removed
- when mouse is over an item, background is destroyed because of repainting the delegate
First of all, am I in the good direction for achieving multiple background images ? If no, could anyone suggests me smarter ways. If yes, what fixes could I imagine!
Thanks
-
The solution was to move all the code into my existing QStyledItemDelegate class. Below, you fill find what I have done (simplified here):
void LibraryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStandardItem *item = _libraryModel.data()->itemFromIndex(_proxy.data()->mapToSource(index)); switch (item->type()) { case Miam::IT_Album: this->paintRect(painter, o); this->drawAlbum(painter, o, static_cast<AlbumItem*>(item)); break; case Miam::IT_Artist: this->paintRect(painter, o); this->drawArtist(painter, o, static_cast<ArtistItem*>(item)); break; case Miam::IT_Disc: this->paintRect(painter, o); this->drawDisc(painter, o, static_cast<DiscItem*>(item)); break; case Miam::IT_Separator: this->drawLetter(painter, o, static_cast<SeparatorItem*>(item)); break; case Miam::IT_Track: this->paintCoverOnTrack(painter, o, static_cast<TrackItem*>(item)); this->drawTrack(painter, o, static_cast<TrackItem*>(item)); break; default: QStyledItemDelegate::paint(painter, o, index); break; }
}
And the function paintCoverOnTrack which does the job:
- Extend the default rect to the left, which is usually where drawBranches do the painting
- Get the picture loaded in memory when expanding the tree. It's loaded only once
- Paint the cover on the right
- Create a subimage to expand it on the left
- Create a selection rectangle if mouse is over
- Then display the text
Here is the code:
void LibraryItemDelegate::paintCoverOnTrack(QPainter *painter, const QStyleOptionViewItem &opt, const TrackItem *track) const { SettingsPrivate *settings = SettingsPrivate::instance(); // Copy QStyleOptionViewItem to be able to expand it to the left, and take the maximum available space QStyleOptionViewItem option(opt); option.rect.setX(0); const QImage *image = _libraryTreeView->expandedCover(track->parent()); if (!image) { return; } int totalHeight = track->model()->rowCount(track->parent()->index()) * option.rect.height(); QImage scaled; QRect subRect; if (totalHeight > option.rect.width()) { scaled = image->scaledToWidth(option.rect.width()); subRect = option.rect.translated(option.rect.width() - scaled.width(), -option.rect.y() + option.rect.height() * track->row()); } else { scaled = image->scaledToHeight(totalHeight); int dx = option.rect.width() - scaled.width(); subRect = option.rect.translated(-dx, -option.rect.y() + option.rect.height() * track->row()); } // Fill with white when there are too much tracks to paint (height of all tracks is greater than the scaled image) QImage subImage = scaled.copy(subRect); if (scaled.height() < subRect.y() + subRect.height()) { subImage.fill(option.palette.base().color()); } painter->save(); painter->setOpacity(1 - settings->bigCoverOpacity()); painter->drawImage(option.rect, subImage); // Over paint black pixel in white QRect t(option.rect.x(), option.rect.y(), option.rect.width() - scaled.width(), option.rect.height()); QImage white(t.size(), QImage::Format_ARGB32); white.fill(option.palette.base().color()); painter->setOpacity(1.0); painter->drawImage(t, white); // Create a mix with 2 images: first one is a 3 pixels subimage of the album cover which is expanded to the left border // The second one is a computer generated gradient focused on alpha channel QImage leftBorder = scaled.copy(0, subRect.y(), 3, option.rect.height()); if (!leftBorder.isNull()) { // Because the expanded border can look strange to one, is blurred with some gaussian function leftBorder = leftBorder.scaled(t.width(), option.rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); leftBorder = ImageUtils::blurred(leftBorder, leftBorder.rect(), 10, false); painter->setOpacity(1 - settings->bigCoverOpacity()); painter->drawImage(t, leftBorder); QLinearGradient linearAlphaBrush(0, 0, leftBorder.width(), 0); linearAlphaBrush.setColorAt(0, QApplication::palette().base().color()); linearAlphaBrush.setColorAt(1, Qt::transparent); painter->setOpacity(1.0); painter->setCompositionMode(QPainter::CompositionMode_SourceOver); painter->setPen(Qt::NoPen); painter->setBrush(linearAlphaBrush); painter->drawRect(t); } painter->restore(); // Display a light selection rectangle when one is moving the cursor painter->save(); QColor color = option.palette.highlight().color(); color.setAlphaF(0.66); if (option.state.testFlag(QStyle::State_MouseOver) && !option.state.testFlag(QStyle::State_Selected)) { if (SettingsPrivate::instance()->isCustomColors()) { painter->setPen(option.palette.highlight().color().darker(100)); painter->setBrush(color.lighter()); } else { painter->setPen(option.palette.highlight().color()); painter->setBrush(color.lighter(160)); } painter->drawRect(opt.rect.adjusted(0, 0, -1, -1)); } else if (option.state.testFlag(QStyle::State_Selected)) { // Display a not so light rectangle when one has chosen an item. It's darker than the mouse over if (SettingsPrivate::instance()->isCustomColors()) { painter->setPen(option.palette.highlight().color().darker(150)); painter->setBrush(color); } else { painter->setPen(option.palette.highlight().color()); painter->setBrush(color.lighter(150)); } painter->drawRect(opt.rect.adjusted(0, 0, -1, -1)); } painter->restore(); }
Here is the result:
-
@Matthieu said:
Glad you found it, Really nice result!
Super Meat Boy ftw