Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. [SOLVED] Multiple background images in QTreeView
Qt 6.11 is out! See what's new in the release blog

[SOLVED] Multiple background images in QTreeView

Scheduled Pinned Locked Moved General and Desktop
qwidget
3 Posts 2 Posters 2.6k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    Matthieu
    wrote on last edited by Matthieu
    #1

    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 :

    Miam-Player

    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

    Open source audio player > http://miam-player.org/

    1 Reply Last reply
    0
    • M Offline
      M Offline
      Matthieu
      wrote on last edited by
      #2

      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:
      Big covers

      Open source audio player > http://miam-player.org/

      1 Reply Last reply
      0
      • M Offline
        M Offline
        maximus
        wrote on last edited by
        #3

        @Matthieu said:

        Glad you found it, Really nice result!
        Super Meat Boy ftw


        Free Indoor Cycling Software - https://maximumtrainer.com

        1 Reply Last reply
        1

        • Login

        • Login or register to search.
        • First post
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • Users
        • Groups
        • Search
        • Get Qt Extensions
        • Unsolved